staging: rts5208: add support for rts5208 and rts5288
authorMicky Ching <micky_ching@realsil.com.cn>
Tue, 12 Nov 2013 09:16:08 +0000 (17:16 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 25 Nov 2013 20:44:37 +0000 (12:44 -0800)
There are still many rts5208/5288 card readers being used, but no
drivers are supported them in kernel now. This driver can make a
great convenience for people who use them.

Many other rts-series card reader are supported by mfd driver, but due
to much difference with others, rts5208/5288 can not add into mfd driver
pretty now, so we provide a separated driver here to support the device.

Signed-off-by: Micky Ching <micky_ching@realsil.com.cn>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
28 files changed:
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/rts5208/Kconfig [new file with mode: 0644]
drivers/staging/rts5208/Makefile [new file with mode: 0644]
drivers/staging/rts5208/TODO [new file with mode: 0644]
drivers/staging/rts5208/debug.h [new file with mode: 0644]
drivers/staging/rts5208/general.c [new file with mode: 0644]
drivers/staging/rts5208/general.h [new file with mode: 0644]
drivers/staging/rts5208/ms.c [new file with mode: 0644]
drivers/staging/rts5208/ms.h [new file with mode: 0644]
drivers/staging/rts5208/rtsx.c [new file with mode: 0644]
drivers/staging/rts5208/rtsx.h [new file with mode: 0644]
drivers/staging/rts5208/rtsx_card.c [new file with mode: 0644]
drivers/staging/rts5208/rtsx_card.h [new file with mode: 0644]
drivers/staging/rts5208/rtsx_chip.c [new file with mode: 0644]
drivers/staging/rts5208/rtsx_chip.h [new file with mode: 0644]
drivers/staging/rts5208/rtsx_scsi.c [new file with mode: 0644]
drivers/staging/rts5208/rtsx_scsi.h [new file with mode: 0644]
drivers/staging/rts5208/rtsx_sys.h [new file with mode: 0644]
drivers/staging/rts5208/rtsx_transport.c [new file with mode: 0644]
drivers/staging/rts5208/rtsx_transport.h [new file with mode: 0644]
drivers/staging/rts5208/sd.c [new file with mode: 0644]
drivers/staging/rts5208/sd.h [new file with mode: 0644]
drivers/staging/rts5208/spi.c [new file with mode: 0644]
drivers/staging/rts5208/spi.h [new file with mode: 0644]
drivers/staging/rts5208/trace.h [new file with mode: 0644]
drivers/staging/rts5208/xd.c [new file with mode: 0644]
drivers/staging/rts5208/xd.h [new file with mode: 0644]

index 3bfdaa8d80a9ccb9b299d6bd4fde693ed4d03652..facc025a5106fbdd4701e9afd08b87cba3f4fe57 100644 (file)
@@ -54,6 +54,8 @@ source "drivers/staging/rtl8188eu/Kconfig"
 
 source "drivers/staging/rts5139/Kconfig"
 
+source "drivers/staging/rts5208/Kconfig"
+
 source "drivers/staging/frontier/Kconfig"
 
 source "drivers/staging/phison/Kconfig"
index b0d3303b46808f224578c3757006aef96099e4cd..094aba9419edd6ad49536bbd50042ec1f41b58d5 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_RTL8192E)                += rtl8192e/
 obj-$(CONFIG_R8712U)           += rtl8712/
 obj-$(CONFIG_R8188EU)          += rtl8188eu/
 obj-$(CONFIG_RTS5139)          += rts5139/
+obj-$(CONFIG_RTS5208)          += rts5208/
 obj-$(CONFIG_TRANZPORT)                += frontier/
 obj-$(CONFIG_IDE_PHISON)       += phison/
 obj-$(CONFIG_LINE6_USB)                += line6/
diff --git a/drivers/staging/rts5208/Kconfig b/drivers/staging/rts5208/Kconfig
new file mode 100644 (file)
index 0000000..055655c
--- /dev/null
@@ -0,0 +1,15 @@
+config RTS5208
+       tristate "Realtek PCI-E Card Reader RTS5208/5288 support"
+       depends on PCI && SCSI
+       help
+         Say Y here to include driver code to support the Realtek
+         PCI-E card reader rts5208/rts5288.
+
+         If this driver is compiled as a module, it will be named rts5208.
+
+config RTS5208_DEBUG
+       bool "Realtek PCI-E Card Reader RTS5208/5288 verbose debug"
+       depends on RTS5208
+       help
+         Say Y here in order to have the rts5208 code generate
+         verbose debugging messages.
diff --git a/drivers/staging/rts5208/Makefile b/drivers/staging/rts5208/Makefile
new file mode 100644 (file)
index 0000000..17b4471
--- /dev/null
@@ -0,0 +1,6 @@
+obj-$(CONFIG_RTS5208) := rts5208.o
+
+ccflags-y := -Idrivers/scsi
+
+rts5208-y := rtsx.o rtsx_chip.o rtsx_transport.o rtsx_scsi.o \
+       rtsx_card.o general.o sd.o xd.o ms.o spi.o
diff --git a/drivers/staging/rts5208/TODO b/drivers/staging/rts5208/TODO
new file mode 100644 (file)
index 0000000..57bcf58
--- /dev/null
@@ -0,0 +1,7 @@
+TODO:
+- use kernel coding style
+- checkpatch.pl fixes
+- We will use the stack in drivers/mmc to implement
+  rts5208/5288 in the future
+
+Micky Ching <micky_ching@realsil.com.cn>
\ No newline at end of file
diff --git a/drivers/staging/rts5208/debug.h b/drivers/staging/rts5208/debug.h
new file mode 100644 (file)
index 0000000..5ba8a3a
--- /dev/null
@@ -0,0 +1,43 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_DEBUG_H
+#define __REALTEK_RTSX_DEBUG_H
+
+#include <linux/kernel.h>
+
+#define RTSX_STOR "rts5208: "
+
+#ifdef CONFIG_RTS5208_DEBUG
+#define RTSX_DEBUGP(x...) pr_debug(RTSX_STOR x)
+#define RTSX_DEBUGPN(x...) pr_debug(x)
+#define RTSX_DEBUGPX(x...) printk(x)
+#define RTSX_DEBUG(x) x
+#else
+#define RTSX_DEBUGP(x...)
+#define RTSX_DEBUGPN(x...)
+#define RTSX_DEBUGPX(x...)
+#define RTSX_DEBUG(x)
+#endif
+
+#endif   /* __REALTEK_RTSX_DEBUG_H */
diff --git a/drivers/staging/rts5208/general.c b/drivers/staging/rts5208/general.c
new file mode 100644 (file)
index 0000000..eada934
--- /dev/null
@@ -0,0 +1,35 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include "general.h"
+
+int bit1cnt_long(u32 data)
+{
+       int i, cnt = 0;
+       for (i = 0; i < 32; i++) {
+               if (data & 0x01)
+                       cnt++;
+               data >>= 1;
+       }
+       return cnt;
+}
+
diff --git a/drivers/staging/rts5208/general.h b/drivers/staging/rts5208/general.h
new file mode 100644 (file)
index 0000000..90a1f92
--- /dev/null
@@ -0,0 +1,31 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __RTSX_GENERAL_H
+#define __RTSX_GENERAL_H
+
+#include "rtsx.h"
+
+int bit1cnt_long(u32 data);
+
+#endif /* __RTSX_GENERAL_H */
diff --git a/drivers/staging/rts5208/ms.c b/drivers/staging/rts5208/ms.c
new file mode 100644 (file)
index 0000000..edf979f
--- /dev/null
@@ -0,0 +1,4208 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#include "rtsx.h"
+#include "rtsx_transport.h"
+#include "rtsx_scsi.h"
+#include "rtsx_card.h"
+#include "ms.h"
+
+static inline void ms_set_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+
+       ms_card->err_code = err_code;
+}
+
+static inline int ms_check_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+
+       return (ms_card->err_code == err_code);
+}
+
+static int ms_parse_err_code(struct rtsx_chip *chip)
+{
+       TRACE_RET(chip, STATUS_FAIL);
+}
+
+static int ms_transfer_tpc(struct rtsx_chip *chip, u8 trans_mode,
+                       u8 tpc, u8 cnt, u8 cfg)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+       u8 *ptr;
+
+       RTSX_DEBUGP("ms_transfer_tpc: tpc = 0x%x\n", tpc);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+               0x01, PINGPONG_BUFFER);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER,
+               0xFF, MS_TRANSFER_START | trans_mode);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER,
+               MS_TRANSFER_END, MS_TRANSFER_END);
+
+       rtsx_add_cmd(chip, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
+
+       retval = rtsx_send_cmd(chip, MS_CARD, 5000);
+       if (retval < 0) {
+               rtsx_clear_ms_error(chip);
+               ms_set_err_code(chip, MS_TO_ERROR);
+               TRACE_RET(chip, ms_parse_err_code(chip));
+       }
+
+       ptr = rtsx_get_cmd_data(chip) + 1;
+
+       if (!(tpc & 0x08)) {            /* Read Packet */
+               if (*ptr & MS_CRC16_ERR) {
+                       ms_set_err_code(chip, MS_CRC16_ERROR);
+                       TRACE_RET(chip, ms_parse_err_code(chip));
+               }
+       } else {                        /* Write Packet */
+               if (CHK_MSPRO(ms_card) && !(*ptr & 0x80)) {
+                       if (*ptr & (MS_INT_ERR | MS_INT_CMDNK)) {
+                               ms_set_err_code(chip, MS_CMD_NK);
+                               TRACE_RET(chip, ms_parse_err_code(chip));
+                       }
+               }
+       }
+
+       if (*ptr & MS_RDY_TIMEOUT) {
+               rtsx_clear_ms_error(chip);
+               ms_set_err_code(chip, MS_TO_ERROR);
+               TRACE_RET(chip, ms_parse_err_code(chip));
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_transfer_data(struct rtsx_chip *chip, u8 trans_mode,
+                       u8 tpc, u16 sec_cnt, u8 cfg, int mode_2k,
+                       int use_sg, void *buf, int buf_len)
+{
+       int retval;
+       u8 val, err_code = 0;
+       enum dma_data_direction dir;
+
+       if (!buf || !buf_len)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (trans_mode == MS_TM_AUTO_READ) {
+               dir = DMA_FROM_DEVICE;
+               err_code = MS_FLASH_READ_ERROR;
+       } else if (trans_mode == MS_TM_AUTO_WRITE) {
+               dir = DMA_TO_DEVICE;
+               err_code = MS_FLASH_WRITE_ERROR;
+       } else {
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+       rtsx_add_cmd(chip, WRITE_REG_CMD,
+                    MS_SECTOR_CNT_H, 0xFF, (u8)(sec_cnt >> 8));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_SECTOR_CNT_L, 0xFF, (u8)sec_cnt);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+
+       if (mode_2k) {
+               rtsx_add_cmd(chip, WRITE_REG_CMD,
+                            MS_CFG, MS_2K_SECTOR_MODE, MS_2K_SECTOR_MODE);
+       } else {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, MS_CFG, MS_2K_SECTOR_MODE, 0);
+       }
+
+       trans_dma_enable(dir, chip, sec_cnt * 512, DMA_512);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD,
+                    MS_TRANSFER, 0xFF, MS_TRANSFER_START | trans_mode);
+       rtsx_add_cmd(chip, CHECK_REG_CMD,
+                    MS_TRANSFER, MS_TRANSFER_END, MS_TRANSFER_END);
+
+       rtsx_send_cmd_no_wait(chip);
+
+       retval = rtsx_transfer_data(chip, MS_CARD, buf, buf_len,
+                                   use_sg, dir, chip->mspro_timeout);
+       if (retval < 0) {
+               ms_set_err_code(chip, err_code);
+               if (retval == -ETIMEDOUT)
+                       retval = STATUS_TIMEDOUT;
+               else
+                       retval = STATUS_FAIL;
+
+               TRACE_RET(chip, retval);
+       }
+
+       RTSX_READ_REG(chip, MS_TRANS_CFG, &val);
+       if (val & (MS_INT_CMDNK | MS_INT_ERR | MS_CRC16_ERR | MS_RDY_TIMEOUT))
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_write_bytes(struct rtsx_chip *chip,
+                         u8 tpc, u8 cnt, u8 cfg, u8 *data, int data_len)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i;
+
+       if (!data || (data_len < cnt))
+               TRACE_RET(chip, STATUS_ERROR);
+
+       rtsx_init_cmd(chip);
+
+       for (i = 0; i < cnt; i++) {
+               rtsx_add_cmd(chip, WRITE_REG_CMD,
+                            PPBUF_BASE2 + i, 0xFF, data[i]);
+       }
+       if (cnt % 2)
+               rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2 + i, 0xFF, 0xFF);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+               0x01, PINGPONG_BUFFER);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD,
+                    MS_TRANSFER, 0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES);
+       rtsx_add_cmd(chip, CHECK_REG_CMD,
+                    MS_TRANSFER, MS_TRANSFER_END, MS_TRANSFER_END);
+
+       retval = rtsx_send_cmd(chip, MS_CARD, 5000);
+       if (retval < 0) {
+               u8 val = 0;
+
+               rtsx_read_register(chip, MS_TRANS_CFG, &val);
+               RTSX_DEBUGP("MS_TRANS_CFG: 0x%02x\n", val);
+
+               rtsx_clear_ms_error(chip);
+
+               if (!(tpc & 0x08)) {
+                       if (val & MS_CRC16_ERR) {
+                               ms_set_err_code(chip, MS_CRC16_ERROR);
+                               TRACE_RET(chip, ms_parse_err_code(chip));
+                       }
+               } else {
+                       if (CHK_MSPRO(ms_card) && !(val & 0x80)) {
+                               if (val & (MS_INT_ERR | MS_INT_CMDNK)) {
+                                       ms_set_err_code(chip, MS_CMD_NK);
+                                       TRACE_RET(chip,
+                                               ms_parse_err_code(chip));
+                               }
+                       }
+               }
+
+               if (val & MS_RDY_TIMEOUT) {
+                       ms_set_err_code(chip, MS_TO_ERROR);
+                       TRACE_RET(chip, ms_parse_err_code(chip));
+               }
+
+               ms_set_err_code(chip, MS_TO_ERROR);
+               TRACE_RET(chip, ms_parse_err_code(chip));
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_read_bytes(struct rtsx_chip *chip,
+                       u8 tpc, u8 cnt, u8 cfg, u8 *data, int data_len)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i;
+       u8 *ptr;
+
+       if (!data)
+               TRACE_RET(chip, STATUS_ERROR);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+               0x01, PINGPONG_BUFFER);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF,
+               MS_TRANSFER_START | MS_TM_READ_BYTES);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER,
+               MS_TRANSFER_END, MS_TRANSFER_END);
+
+       for (i = 0; i < data_len - 1; i++)
+              rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0);
+
+       if (data_len % 2)
+               rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + data_len, 0, 0);
+       else
+               rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + data_len - 1,
+                       0, 0);
+
+       retval = rtsx_send_cmd(chip, MS_CARD, 5000);
+       if (retval < 0) {
+               u8 val = 0;
+
+               rtsx_read_register(chip, MS_TRANS_CFG, &val);
+               rtsx_clear_ms_error(chip);
+
+               if (!(tpc & 0x08)) {
+                       if (val & MS_CRC16_ERR) {
+                               ms_set_err_code(chip, MS_CRC16_ERROR);
+                               TRACE_RET(chip, ms_parse_err_code(chip));
+                       }
+               } else {
+                       if (CHK_MSPRO(ms_card) && !(val & 0x80)) {
+                               if (val & (MS_INT_ERR | MS_INT_CMDNK)) {
+                                       ms_set_err_code(chip, MS_CMD_NK);
+                                       TRACE_RET(chip,
+                                               ms_parse_err_code(chip));
+                               }
+                       }
+               }
+
+               if (val & MS_RDY_TIMEOUT) {
+                       ms_set_err_code(chip, MS_TO_ERROR);
+                       TRACE_RET(chip, ms_parse_err_code(chip));
+               }
+
+               ms_set_err_code(chip, MS_TO_ERROR);
+               TRACE_RET(chip, ms_parse_err_code(chip));
+       }
+
+       ptr = rtsx_get_cmd_data(chip) + 1;
+
+       for (i = 0; i < data_len; i++)
+               data[i] = ptr[i];
+
+       if ((tpc == PRO_READ_SHORT_DATA) && (data_len == 8)) {
+               RTSX_DEBUGP("Read format progress:\n");
+               RTSX_DUMP(ptr, cnt);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_set_rw_reg_addr(struct rtsx_chip *chip,
+               u8 read_start, u8 read_cnt, u8 write_start, u8 write_cnt)
+{
+       int retval, i;
+       u8 data[4];
+
+       data[0] = read_start;
+       data[1] = read_cnt;
+       data[2] = write_start;
+       data[3] = write_cnt;
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_write_bytes(chip, SET_RW_REG_ADRS, 4,
+                                       NO_WAIT_INT, data, 4);
+               if (retval == STATUS_SUCCESS)
+                       return STATUS_SUCCESS;
+               rtsx_clear_ms_error(chip);
+       }
+
+       TRACE_RET(chip, STATUS_FAIL);
+}
+
+static int ms_send_cmd(struct rtsx_chip *chip, u8 cmd, u8 cfg)
+{
+       u8 data[2];
+
+       data[0] = cmd;
+       data[1] = 0;
+
+       return ms_write_bytes(chip, PRO_SET_CMD, 1, cfg, data, 1);
+}
+
+static int ms_set_init_para(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+
+       if (CHK_HG8BIT(ms_card)) {
+               if (chip->asic_code)
+                       ms_card->ms_clock = chip->asic_ms_hg_clk;
+               else
+                       ms_card->ms_clock = chip->fpga_ms_hg_clk;
+
+       } else if (CHK_MSPRO(ms_card) || CHK_MS4BIT(ms_card)) {
+               if (chip->asic_code)
+                       ms_card->ms_clock = chip->asic_ms_4bit_clk;
+               else
+                       ms_card->ms_clock = chip->fpga_ms_4bit_clk;
+
+       } else {
+               if (chip->asic_code)
+                       ms_card->ms_clock = chip->asic_ms_1bit_clk;
+               else
+                       ms_card->ms_clock = chip->fpga_ms_1bit_clk;
+       }
+
+       retval = switch_clock(chip, ms_card->ms_clock);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = select_card(chip, MS_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_switch_clock(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+
+       retval = select_card(chip, MS_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = switch_clock(chip, ms_card->ms_clock);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_pull_ctl_disable(struct rtsx_chip *chip)
+{
+       if (CHECK_PID(chip, 0x5208)) {
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL1, 0xFF,
+                       MS_D1_PD | MS_D2_PD | MS_CLK_PD | MS_D6_PD);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL2, 0xFF,
+                       MS_D3_PD | MS_D0_PD | MS_BS_PD | XD_D4_PD);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL3, 0xFF,
+                       MS_D7_PD | XD_CE_PD | XD_CLE_PD | XD_CD_PU);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL4, 0xFF,
+                       XD_RDY_PD | SD_D3_PD | SD_D2_PD | XD_ALE_PD);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL5, 0xFF,
+                       MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL6, 0xFF,
+                       MS_D5_PD | MS_D4_PD);
+       } else if (CHECK_PID(chip, 0x5288)) {
+               if (CHECK_BARO_PKG(chip, QFN)) {
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL1, 0xFF, 0x55);
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL2, 0xFF, 0x55);
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL3, 0xFF, 0x4B);
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL4, 0xFF, 0x69);
+               }
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_pull_ctl_enable(struct rtsx_chip *chip)
+{
+       int retval;
+
+       rtsx_init_cmd(chip);
+
+       if (CHECK_PID(chip, 0x5208)) {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF,
+                       MS_D1_PD | MS_D2_PD | MS_CLK_NP | MS_D6_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF,
+                       MS_D3_PD | MS_D0_PD | MS_BS_NP | XD_D4_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF,
+                       MS_D7_PD | XD_CE_PD | XD_CLE_PD | XD_CD_PU);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF,
+                       XD_RDY_PD | SD_D3_PD | SD_D2_PD | XD_ALE_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF,
+                       MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF,
+                       MS_D5_PD | MS_D4_PD);
+       } else if (CHECK_PID(chip, 0x5288)) {
+               if (CHECK_BARO_PKG(chip, QFN)) {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD,
+                                    CARD_PULL_CTL1, 0xFF, 0x55);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD,
+                                    CARD_PULL_CTL2, 0xFF, 0x45);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD,
+                                    CARD_PULL_CTL3, 0xFF, 0x4B);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD,
+                                    CARD_PULL_CTL4, 0xFF, 0x29);
+               }
+       }
+
+       retval = rtsx_send_cmd(chip, MS_CARD, 100);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_prepare_reset(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+       u8 oc_mask = 0;
+
+       ms_card->ms_type = 0;
+       ms_card->check_ms_flow = 0;
+       ms_card->switch_8bit_fail = 0;
+       ms_card->delay_write.delay_write_flag = 0;
+
+       ms_card->pro_under_formatting = 0;
+
+       retval = ms_power_off_card3v3(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (!chip->ft2_fast_mode)
+               wait_timeout(250);
+
+       retval = enable_card_clock(chip, MS_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (chip->asic_code) {
+               retval = ms_pull_ctl_enable(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       } else {
+               RTSX_WRITE_REG(chip, FPGA_PULL_CTL,
+                       FPGA_MS_PULL_CTL_BIT | 0x20, 0);
+       }
+
+       if (!chip->ft2_fast_mode) {
+               retval = card_power_on(chip, MS_CARD);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               wait_timeout(150);
+
+#ifdef SUPPORT_OCP
+               if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
+                       oc_mask = MS_OC_NOW | MS_OC_EVER;
+               else
+                       oc_mask = SD_OC_NOW | SD_OC_EVER;
+
+               if (chip->ocp_stat & oc_mask) {
+                       RTSX_DEBUGP("Over current, OCPSTAT is 0x%x\n",
+                                    chip->ocp_stat);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+#endif
+       }
+
+       RTSX_WRITE_REG(chip, CARD_OE, MS_OUTPUT_EN, MS_OUTPUT_EN);
+
+       if (chip->asic_code) {
+               RTSX_WRITE_REG(chip, MS_CFG, 0xFF,
+                       SAMPLE_TIME_RISING | PUSH_TIME_DEFAULT |
+                       NO_EXTEND_TOGGLE | MS_BUS_WIDTH_1);
+       } else {
+               RTSX_WRITE_REG(chip, MS_CFG, 0xFF,
+                       SAMPLE_TIME_FALLING | PUSH_TIME_DEFAULT |
+                       NO_EXTEND_TOGGLE | MS_BUS_WIDTH_1);
+       }
+       RTSX_WRITE_REG(chip, MS_TRANS_CFG,
+               0xFF, NO_WAIT_INT | NO_AUTO_READ_INT_REG);
+       RTSX_WRITE_REG(chip, CARD_STOP,
+               MS_STOP | MS_CLR_ERR, MS_STOP | MS_CLR_ERR);
+
+       retval = ms_set_init_para(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_identify_media_type(struct rtsx_chip *chip, int switch_8bit_bus)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i;
+       u8 val;
+
+       retval = ms_set_rw_reg_addr(chip, Pro_StatusReg, 6, SystemParm, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_transfer_tpc(chip, MS_TM_READ_BYTES, READ_REG,
+                                       6, NO_WAIT_INT);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (i == MS_MAX_RETRY_COUNT)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_READ_REG(chip, PPBUF_BASE2 + 2, &val);
+       RTSX_DEBUGP("Type register: 0x%x\n", val);
+       if (val != 0x01) {
+               if (val != 0x02)
+                       ms_card->check_ms_flow = 1;
+
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       RTSX_READ_REG(chip, PPBUF_BASE2 + 4, &val);
+       RTSX_DEBUGP("Category register: 0x%x\n", val);
+       if (val != 0) {
+               ms_card->check_ms_flow = 1;
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       RTSX_READ_REG(chip, PPBUF_BASE2 + 5, &val);
+       RTSX_DEBUGP("Class register: 0x%x\n", val);
+       if (val == 0) {
+               RTSX_READ_REG(chip, PPBUF_BASE2, &val);
+               if (val & WRT_PRTCT)
+                       chip->card_wp |= MS_CARD;
+               else
+                       chip->card_wp &= ~MS_CARD;
+
+       } else if ((val == 0x01) || (val == 0x02) || (val == 0x03)) {
+               chip->card_wp |= MS_CARD;
+       } else {
+               ms_card->check_ms_flow = 1;
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       ms_card->ms_type |= TYPE_MSPRO;
+
+       RTSX_READ_REG(chip, PPBUF_BASE2 + 3, &val);
+       RTSX_DEBUGP("IF Mode register: 0x%x\n", val);
+       if (val == 0) {
+               ms_card->ms_type &= 0x0F;
+       } else if (val == 7) {
+               if (switch_8bit_bus)
+                       ms_card->ms_type |= MS_HG;
+               else
+                       ms_card->ms_type &= 0x0F;
+
+       } else {
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_confirm_cpu_startup(struct rtsx_chip *chip)
+{
+       int retval, i, k;
+       u8 val;
+
+       /* Confirm CPU StartUp */
+       k = 0;
+       do {
+               if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+                       ms_set_err_code(chip, MS_NO_CARD);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+                       retval = ms_read_bytes(chip, GET_INT, 1,
+                                       NO_WAIT_INT, &val, 1);
+                       if (retval == STATUS_SUCCESS)
+                               break;
+               }
+               if (i == MS_MAX_RETRY_COUNT)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if (k > 100)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               k++;
+               wait_timeout(100);
+       } while (!(val & INT_REG_CED));
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (i == MS_MAX_RETRY_COUNT)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (val & INT_REG_ERR) {
+               if (val & INT_REG_CMDNK)
+                       chip->card_wp |= (MS_CARD);
+               else
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+       /* --  end confirm CPU startup */
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_switch_parallel_bus(struct rtsx_chip *chip)
+{
+       int retval, i;
+       u8 data[2];
+
+       data[0] = PARALLEL_4BIT_IF;
+       data[1] = 0;
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_write_bytes(chip, WRITE_REG, 1, NO_WAIT_INT,
+                                       data, 2);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_switch_8bit_bus(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i;
+       u8 data[2];
+
+       data[0] = PARALLEL_8BIT_IF;
+       data[1] = 0;
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_write_bytes(chip, WRITE_REG, 1,
+                                       NO_WAIT_INT, data, 2);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_WRITE_REG(chip, MS_CFG, 0x98,
+               MS_BUS_WIDTH_8 | SAMPLE_TIME_FALLING);
+       ms_card->ms_type |= MS_8BIT;
+       retval = ms_set_init_para(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_transfer_tpc(chip, MS_TM_READ_BYTES, GET_INT,
+                                       1, NO_WAIT_INT);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_pro_reset_flow(struct rtsx_chip *chip, int switch_8bit_bus)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i;
+
+       for (i = 0; i < 3; i++) {
+               retval = ms_prepare_reset(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = ms_identify_media_type(chip, switch_8bit_bus);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = ms_confirm_cpu_startup(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = ms_switch_parallel_bus(chip);
+               if (retval != STATUS_SUCCESS) {
+                       if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+                               ms_set_err_code(chip, MS_NO_CARD);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       continue;
+               } else {
+                       break;
+               }
+       }
+
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       /* Switch MS-PRO into Parallel mode */
+       RTSX_WRITE_REG(chip, MS_CFG, 0x18, MS_BUS_WIDTH_4);
+       RTSX_WRITE_REG(chip, MS_CFG, PUSH_TIME_ODD, PUSH_TIME_ODD);
+
+       retval = ms_set_init_para(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       /* If MSPro HG Card, We shall try to switch to 8-bit bus */
+       if (CHK_MSHG(ms_card) && chip->support_ms_8bit && switch_8bit_bus) {
+               retval = ms_switch_8bit_bus(chip);
+               if (retval != STATUS_SUCCESS) {
+                       ms_card->switch_8bit_fail = 1;
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       return STATUS_SUCCESS;
+}
+
+#ifdef XC_POWERCLASS
+static int msxc_change_power(struct rtsx_chip *chip, u8 mode)
+{
+       int retval;
+       u8 buf[6];
+
+       ms_cleanup_work(chip);
+
+       retval = ms_set_rw_reg_addr(chip, 0, 0, Pro_DataCount1, 6);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       buf[0] = 0;
+       buf[1] = mode;
+       buf[2] = 0;
+       buf[3] = 0;
+       buf[4] = 0;
+       buf[5] = 0;
+
+       retval = ms_write_bytes(chip, PRO_WRITE_REG , 6, NO_WAIT_INT, buf, 6);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = ms_send_cmd(chip, XC_CHG_POWER, WAIT_INT);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_READ_REG(chip, MS_TRANS_CFG, buf);
+       if (buf[0] & (MS_INT_CMDNK | MS_INT_ERR))
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+#endif
+
+static int ms_read_attribute_info(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i;
+       u8 val, *buf, class_code, device_type, sub_class, data[16];
+       u16 total_blk = 0, blk_size = 0;
+#ifdef SUPPORT_MSXC
+       u32 xc_total_blk = 0, xc_blk_size = 0;
+#endif
+       u32 sys_info_addr = 0, sys_info_size;
+#ifdef SUPPORT_PCGL_1P18
+       u32 model_name_addr = 0, model_name_size;
+       int found_sys_info = 0, found_model_name = 0;
+#endif
+
+       retval = ms_set_rw_reg_addr(chip, Pro_IntReg, 2, Pro_SystemParm, 7);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (CHK_MS8BIT(ms_card))
+               data[0] = PARALLEL_8BIT_IF;
+       else
+               data[0] = PARALLEL_4BIT_IF;
+
+       data[1] = 0;
+
+       data[2] = 0x40;
+       data[3] = 0;
+       data[4] = 0;
+       data[5] = 0;
+       data[6] = 0;
+       data[7] = 0;
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_write_bytes(chip, PRO_WRITE_REG, 7, NO_WAIT_INT,
+                                       data, 8);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       buf = kmalloc(64 * 512, GFP_KERNEL);
+       if (buf == NULL)
+               TRACE_RET(chip, STATUS_ERROR);
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_send_cmd(chip, PRO_READ_ATRB, WAIT_INT);
+               if (retval != STATUS_SUCCESS)
+                       continue;
+
+               retval = rtsx_read_register(chip, MS_TRANS_CFG, &val);
+               if (retval != STATUS_SUCCESS) {
+                       kfree(buf);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+               if (!(val & MS_INT_BREQ)) {
+                       kfree(buf);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+               retval = ms_transfer_data(chip, MS_TM_AUTO_READ,
+                                       PRO_READ_LONG_DATA, 0x40, WAIT_INT,
+                                       0, 0, buf, 64 * 512);
+               if (retval == STATUS_SUCCESS)
+                       break;
+               else
+                       rtsx_clear_ms_error(chip);
+       }
+       if (retval != STATUS_SUCCESS) {
+               kfree(buf);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       i = 0;
+       do {
+               retval = rtsx_read_register(chip, MS_TRANS_CFG, &val);
+               if (retval != STATUS_SUCCESS) {
+                       kfree(buf);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               if ((val & MS_INT_CED) || !(val & MS_INT_BREQ))
+                       break;
+
+               retval = ms_transfer_tpc(chip, MS_TM_NORMAL_READ,
+                                       PRO_READ_LONG_DATA, 0, WAIT_INT);
+               if (retval != STATUS_SUCCESS) {
+                       kfree(buf);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               i++;
+       } while (i < 1024);
+
+       if (retval != STATUS_SUCCESS) {
+               kfree(buf);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if ((buf[0] != 0xa5) && (buf[1] != 0xc3)) {
+               /* Signature code is wrong */
+               kfree(buf);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if ((buf[4] < 1) || (buf[4] > 12)) {
+               kfree(buf);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       for (i = 0; i < buf[4]; i++) {
+               int cur_addr_off = 16 + i * 12;
+
+#ifdef SUPPORT_MSXC
+               if ((buf[cur_addr_off + 8] == 0x10) ||
+                       (buf[cur_addr_off + 8] == 0x13))
+#else
+               if (buf[cur_addr_off + 8] == 0x10)
+#endif
+               {
+                       sys_info_addr = ((u32)buf[cur_addr_off + 0] << 24) |
+                               ((u32)buf[cur_addr_off + 1] << 16) |
+                               ((u32)buf[cur_addr_off + 2] << 8) |
+                               buf[cur_addr_off + 3];
+                       sys_info_size = ((u32)buf[cur_addr_off + 4] << 24) |
+                               ((u32)buf[cur_addr_off + 5] << 16) |
+                               ((u32)buf[cur_addr_off + 6] << 8) |
+                               buf[cur_addr_off + 7];
+                       RTSX_DEBUGP("sys_info_addr = 0x%x, sys_info_size = 0x%x\n",
+                               sys_info_addr, sys_info_size);
+                       if (sys_info_size != 96)  {
+                               kfree(buf);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       if (sys_info_addr < 0x1A0) {
+                               kfree(buf);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       if ((sys_info_size + sys_info_addr) > 0x8000) {
+                               kfree(buf);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+#ifdef SUPPORT_MSXC
+                       if (buf[cur_addr_off + 8] == 0x13)
+                               ms_card->ms_type |= MS_XC;
+#endif
+#ifdef SUPPORT_PCGL_1P18
+                       found_sys_info = 1;
+#else
+                       break;
+#endif
+               }
+#ifdef SUPPORT_PCGL_1P18
+               if (buf[cur_addr_off + 8] == 0x15) {
+                       model_name_addr = ((u32)buf[cur_addr_off + 0] << 24) |
+                               ((u32)buf[cur_addr_off + 1] << 16) |
+                               ((u32)buf[cur_addr_off + 2] << 8) |
+                               buf[cur_addr_off + 3];
+                       model_name_size = ((u32)buf[cur_addr_off + 4] << 24) |
+                               ((u32)buf[cur_addr_off + 5] << 16) |
+                               ((u32)buf[cur_addr_off + 6] << 8) |
+                               buf[cur_addr_off + 7];
+                       RTSX_DEBUGP("model_name_addr = 0x%x, model_name_size = 0x%x\n",
+                               model_name_addr, model_name_size);
+                       if (model_name_size != 48)  {
+                               kfree(buf);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       if (model_name_addr < 0x1A0) {
+                               kfree(buf);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       if ((model_name_size + model_name_addr) > 0x8000) {
+                               kfree(buf);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       found_model_name = 1;
+               }
+
+               if (found_sys_info && found_model_name)
+                       break;
+#endif
+       }
+
+       if (i == buf[4]) {
+               kfree(buf);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       class_code =  buf[sys_info_addr + 0];
+       device_type = buf[sys_info_addr + 56];
+       sub_class = buf[sys_info_addr + 46];
+#ifdef SUPPORT_MSXC
+       if (CHK_MSXC(ms_card)) {
+               xc_total_blk = ((u32)buf[sys_info_addr + 6] << 24) |
+                               ((u32)buf[sys_info_addr + 7] << 16) |
+                               ((u32)buf[sys_info_addr + 8] << 8) |
+                               buf[sys_info_addr + 9];
+               xc_blk_size = ((u32)buf[sys_info_addr + 32] << 24) |
+                               ((u32)buf[sys_info_addr + 33] << 16) |
+                               ((u32)buf[sys_info_addr + 34] << 8) |
+                               buf[sys_info_addr + 35];
+               RTSX_DEBUGP("xc_total_blk = 0x%x, xc_blk_size = 0x%x\n",
+                       xc_total_blk, xc_blk_size);
+       } else {
+               total_blk = ((u16)buf[sys_info_addr + 6] << 8) |
+                       buf[sys_info_addr + 7];
+               blk_size = ((u16)buf[sys_info_addr + 2] << 8) |
+                       buf[sys_info_addr + 3];
+               RTSX_DEBUGP("total_blk = 0x%x, blk_size = 0x%x\n",
+                       total_blk, blk_size);
+       }
+#else
+       total_blk = ((u16)buf[sys_info_addr + 6] << 8) | buf[sys_info_addr + 7];
+       blk_size = ((u16)buf[sys_info_addr + 2] << 8) | buf[sys_info_addr + 3];
+       RTSX_DEBUGP("total_blk = 0x%x, blk_size = 0x%x\n", total_blk, blk_size);
+#endif
+
+       RTSX_DEBUGP("class_code = 0x%x, device_type = 0x%x, sub_class = 0x%x\n",
+                       class_code, device_type, sub_class);
+
+       memcpy(ms_card->raw_sys_info, buf + sys_info_addr, 96);
+#ifdef SUPPORT_PCGL_1P18
+       memcpy(ms_card->raw_model_name, buf + model_name_addr, 48);
+#endif
+
+       kfree(buf);
+
+#ifdef SUPPORT_MSXC
+       if (CHK_MSXC(ms_card)) {
+               if (class_code != 0x03)
+                       TRACE_RET(chip, STATUS_FAIL);
+       } else {
+               if (class_code != 0x02)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+#else
+       if (class_code != 0x02)
+               TRACE_RET(chip, STATUS_FAIL);
+#endif
+
+       if (device_type != 0x00) {
+               if ((device_type == 0x01) || (device_type == 0x02) ||
+                               (device_type == 0x03)) {
+                       chip->card_wp |= MS_CARD;
+               } else {
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       if (sub_class & 0xC0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_DEBUGP("class_code: 0x%x, device_type: 0x%x, sub_class: 0x%x\n",
+               class_code, device_type, sub_class);
+
+#ifdef SUPPORT_MSXC
+       if (CHK_MSXC(ms_card)) {
+               chip->capacity[chip->card2lun[MS_CARD]] =
+                       ms_card->capacity = xc_total_blk * xc_blk_size;
+       } else {
+               chip->capacity[chip->card2lun[MS_CARD]] =
+                       ms_card->capacity = total_blk * blk_size;
+       }
+#else
+       ms_card->capacity = total_blk * blk_size;
+       chip->capacity[chip->card2lun[MS_CARD]] = ms_card->capacity;
+#endif
+
+       return STATUS_SUCCESS;
+}
+
+#ifdef SUPPORT_MAGIC_GATE
+static int mg_set_tpc_para_sub(struct rtsx_chip *chip,
+                       int type, u8 mg_entry_num);
+#endif
+
+static int reset_ms_pro(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+#ifdef XC_POWERCLASS
+       u8 change_power_class;
+
+       if (chip->ms_power_class_en & 0x02)
+               change_power_class = 2;
+       else if (chip->ms_power_class_en & 0x01)
+               change_power_class = 1;
+       else
+               change_power_class = 0;
+#endif
+
+#ifdef XC_POWERCLASS
+Retry:
+#endif
+       retval = ms_pro_reset_flow(chip, 1);
+       if (retval != STATUS_SUCCESS) {
+               if (ms_card->switch_8bit_fail) {
+                       retval = ms_pro_reset_flow(chip, 0);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+               } else {
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       retval = ms_read_attribute_info(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+#ifdef XC_POWERCLASS
+       if (CHK_HG8BIT(ms_card))
+               change_power_class = 0;
+
+       if (change_power_class && CHK_MSXC(ms_card)) {
+               u8 power_class_en = chip->ms_power_class_en;
+
+               RTSX_DEBUGP("power_class_en = 0x%x\n", power_class_en);
+               RTSX_DEBUGP("change_power_class = %d\n", change_power_class);
+
+               if (change_power_class)
+                       power_class_en &= (1 << (change_power_class - 1));
+               else
+                       power_class_en = 0;
+
+               if (power_class_en) {
+                       u8 power_class_mode =
+                               (ms_card->raw_sys_info[46] & 0x18) >> 3;
+                       RTSX_DEBUGP("power_class_mode = 0x%x",
+                               power_class_mode);
+                       if (change_power_class > power_class_mode)
+                               change_power_class = power_class_mode;
+                       if (change_power_class) {
+                               retval = msxc_change_power(chip,
+                                                       change_power_class);
+                               if (retval != STATUS_SUCCESS) {
+                                       change_power_class--;
+                                       goto Retry;
+                               }
+                       }
+               }
+       }
+#endif
+
+#ifdef SUPPORT_MAGIC_GATE
+       retval = mg_set_tpc_para_sub(chip, 0, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+#endif
+
+       if (CHK_HG8BIT(ms_card))
+               chip->card_bus_width[chip->card2lun[MS_CARD]] = 8;
+       else
+               chip->card_bus_width[chip->card2lun[MS_CARD]] = 4;
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_read_status_reg(struct rtsx_chip *chip)
+{
+       int retval;
+       u8 val[2];
+
+       retval = ms_set_rw_reg_addr(chip, StatusReg0, 2, 0, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = ms_read_bytes(chip, READ_REG, 2, NO_WAIT_INT, val, 2);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (val[1] & (STS_UCDT | STS_UCEX | STS_UCFG)) {
+               ms_set_err_code(chip, MS_FLASH_READ_ERROR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+
+static int ms_read_extra_data(struct rtsx_chip *chip,
+               u16 block_addr, u8 page_num, u8 *buf, int buf_len)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i;
+       u8 val, data[10];
+
+       retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+                               SystemParm, 6);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (CHK_MS4BIT(ms_card)) {
+               /* Parallel interface */
+               data[0] = 0x88;
+       } else {
+               /* Serial interface */
+               data[0] = 0x80;
+       }
+       data[1] = 0;
+       data[2] = (u8)(block_addr >> 8);
+       data[3] = (u8)block_addr;
+       data[4] = 0x40;
+       data[5] = page_num;
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT,
+                                       data, 6);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (i == MS_MAX_RETRY_COUNT)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ms_set_err_code(chip, MS_NO_ERROR);
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (i == MS_MAX_RETRY_COUNT)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ms_set_err_code(chip, MS_NO_ERROR);
+       retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (val & INT_REG_CMDNK) {
+               ms_set_err_code(chip, MS_CMD_NK);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+       if (val & INT_REG_CED) {
+               if (val & INT_REG_ERR) {
+                       retval = ms_read_status_reg(chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       retval = ms_set_rw_reg_addr(chip, OverwriteFlag,
+                                               MS_EXTRA_SIZE, SystemParm, 6);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       retval = ms_read_bytes(chip, READ_REG, MS_EXTRA_SIZE, NO_WAIT_INT,
+                       data, MS_EXTRA_SIZE);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (buf && buf_len) {
+               if (buf_len > MS_EXTRA_SIZE)
+                       buf_len = MS_EXTRA_SIZE;
+               memcpy(buf, data, buf_len);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_write_extra_data(struct rtsx_chip *chip,
+               u16 block_addr, u8 page_num, u8 *buf, int buf_len)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i;
+       u8 val, data[16];
+
+       if (!buf || (buf_len < MS_EXTRA_SIZE))
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+                               SystemParm, 6 + MS_EXTRA_SIZE);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (CHK_MS4BIT(ms_card))
+               data[0] = 0x88;
+       else
+               data[0] = 0x80;
+
+       data[1] = 0;
+       data[2] = (u8)(block_addr >> 8);
+       data[3] = (u8)block_addr;
+       data[4] = 0x40;
+       data[5] = page_num;
+
+       for (i = 6; i < MS_EXTRA_SIZE + 6; i++)
+               data[i] = buf[i - 6];
+
+       retval = ms_write_bytes(chip, WRITE_REG , (6+MS_EXTRA_SIZE),
+                               NO_WAIT_INT, data, 16);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ms_set_err_code(chip, MS_NO_ERROR);
+       retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (val & INT_REG_CMDNK) {
+               ms_set_err_code(chip, MS_CMD_NK);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+       if (val & INT_REG_CED) {
+               if (val & INT_REG_ERR) {
+                       ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       return STATUS_SUCCESS;
+}
+
+
+static int ms_read_page(struct rtsx_chip *chip, u16 block_addr, u8 page_num)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+       u8 val, data[6];
+
+       retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+                               SystemParm, 6);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (CHK_MS4BIT(ms_card))
+               data[0] = 0x88;
+       else
+               data[0] = 0x80;
+
+       data[1] = 0;
+       data[2] = (u8)(block_addr >> 8);
+       data[3] = (u8)block_addr;
+       data[4] = 0x20;
+       data[5] = page_num;
+
+       retval = ms_write_bytes(chip, WRITE_REG , 6, NO_WAIT_INT, data, 6);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ms_set_err_code(chip, MS_NO_ERROR);
+       retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (val & INT_REG_CMDNK) {
+               ms_set_err_code(chip, MS_CMD_NK);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (val & INT_REG_CED) {
+               if (val & INT_REG_ERR) {
+                       if (!(val & INT_REG_BREQ)) {
+                               ms_set_err_code(chip,  MS_FLASH_READ_ERROR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       retval = ms_read_status_reg(chip);
+                       if (retval != STATUS_SUCCESS)
+                               ms_set_err_code(chip,  MS_FLASH_WRITE_ERROR);
+
+               } else {
+                       if (!(val & INT_REG_BREQ)) {
+                               ms_set_err_code(chip, MS_BREQ_ERROR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+       }
+
+       retval = ms_transfer_tpc(chip, MS_TM_NORMAL_READ, READ_PAGE_DATA,
+                               0, NO_WAIT_INT);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (ms_check_err_code(chip, MS_FLASH_WRITE_ERROR))
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+
+static int ms_set_bad_block(struct rtsx_chip *chip, u16 phy_blk)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+       u8 val, data[8], extra[MS_EXTRA_SIZE];
+
+       retval = ms_read_extra_data(chip, phy_blk, 0, extra, MS_EXTRA_SIZE);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+                               SystemParm, 7);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ms_set_err_code(chip, MS_NO_ERROR);
+
+       if (CHK_MS4BIT(ms_card))
+               data[0] = 0x88;
+       else
+               data[0] = 0x80;
+
+       data[1] = 0;
+       data[2] = (u8)(phy_blk >> 8);
+       data[3] = (u8)phy_blk;
+       data[4] = 0x80;
+       data[5] = 0;
+       data[6] = extra[0] & 0x7F;
+       data[7] = 0xFF;
+
+       retval = ms_write_bytes(chip, WRITE_REG, 7, NO_WAIT_INT, data, 7);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ms_set_err_code(chip, MS_NO_ERROR);
+       retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (val & INT_REG_CMDNK) {
+               ms_set_err_code(chip, MS_CMD_NK);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (val & INT_REG_CED) {
+               if (val & INT_REG_ERR) {
+                       ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       return STATUS_SUCCESS;
+}
+
+
+static int ms_erase_block(struct rtsx_chip *chip, u16 phy_blk)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i = 0;
+       u8 val, data[6];
+
+       retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+                               SystemParm, 6);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ms_set_err_code(chip, MS_NO_ERROR);
+
+       if (CHK_MS4BIT(ms_card))
+               data[0] = 0x88;
+       else
+               data[0] = 0x80;
+
+       data[1] = 0;
+       data[2] = (u8)(phy_blk >> 8);
+       data[3] = (u8)phy_blk;
+       data[4] = 0;
+       data[5] = 0;
+
+       retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT, data, 6);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+ERASE_RTY:
+       retval = ms_send_cmd(chip, BLOCK_ERASE, WAIT_INT);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ms_set_err_code(chip, MS_NO_ERROR);
+       retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (val & INT_REG_CMDNK) {
+               if (i < 3) {
+                       i++;
+                       goto ERASE_RTY;
+               }
+
+               ms_set_err_code(chip, MS_CMD_NK);
+               ms_set_bad_block(chip, phy_blk);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (val & INT_REG_CED) {
+               if (val & INT_REG_ERR) {
+                       ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       return STATUS_SUCCESS;
+}
+
+
+static void ms_set_page_status(u16 log_blk, u8 type, u8 *extra, int extra_len)
+{
+       if (!extra || (extra_len < MS_EXTRA_SIZE))
+               return;
+
+       memset(extra, 0xFF, MS_EXTRA_SIZE);
+
+       if (type == setPS_NG) {
+               /* set page status as 1:NG,and block status keep 1:OK */
+               extra[0] = 0xB8;
+       } else {
+               /* set page status as 0:Data Error,and block status keep 1:OK */
+               extra[0] = 0x98;
+       }
+
+       extra[2] = (u8)(log_blk >> 8);
+       extra[3] = (u8)log_blk;
+}
+
+static int ms_init_page(struct rtsx_chip *chip, u16 phy_blk, u16 log_blk,
+                       u8 start_page, u8 end_page)
+{
+       int retval;
+       u8 extra[MS_EXTRA_SIZE], i;
+
+       memset(extra, 0xff, MS_EXTRA_SIZE);
+
+       extra[0] = 0xf8;        /* Block, page OK, data erased */
+       extra[1] = 0xff;
+       extra[2] = (u8)(log_blk >> 8);
+       extra[3] = (u8)log_blk;
+
+       for (i = start_page; i < end_page; i++) {
+               if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+                       ms_set_err_code(chip, MS_NO_CARD);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               retval = ms_write_extra_data(chip, phy_blk, i,
+                                       extra, MS_EXTRA_SIZE);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_copy_page(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
+               u16 log_blk, u8 start_page, u8 end_page)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, rty_cnt, uncorrect_flag = 0;
+       u8 extra[MS_EXTRA_SIZE], val, i, j, data[16];
+
+       RTSX_DEBUGP("Copy page from 0x%x to 0x%x, logical block is 0x%x\n",
+               old_blk, new_blk, log_blk);
+       RTSX_DEBUGP("start_page = %d, end_page = %d\n", start_page, end_page);
+
+       retval = ms_read_extra_data(chip, new_blk, 0, extra, MS_EXTRA_SIZE);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = ms_read_status_reg(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_READ_REG(chip, PPBUF_BASE2, &val);
+
+       if (val & BUF_FULL) {
+               retval = ms_send_cmd(chip, CLEAR_BUF, WAIT_INT);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if (!(val & INT_REG_CED)) {
+                       ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       for (i = start_page; i < end_page; i++) {
+               if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+                       ms_set_err_code(chip, MS_NO_CARD);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               ms_read_extra_data(chip, old_blk, i, extra, MS_EXTRA_SIZE);
+
+               retval = ms_set_rw_reg_addr(chip, OverwriteFlag,
+                                       MS_EXTRA_SIZE, SystemParm, 6);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               ms_set_err_code(chip, MS_NO_ERROR);
+
+               if (CHK_MS4BIT(ms_card))
+                       data[0] = 0x88;
+               else
+                       data[0] = 0x80;
+
+               data[1] = 0;
+               data[2] = (u8)(old_blk >> 8);
+               data[3] = (u8)old_blk;
+               data[4] = 0x20;
+               data[5] = i;
+
+               retval = ms_write_bytes(chip, WRITE_REG , 6, NO_WAIT_INT,
+                                       data, 6);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               ms_set_err_code(chip, MS_NO_ERROR);
+               retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if (val & INT_REG_CMDNK) {
+                       ms_set_err_code(chip, MS_CMD_NK);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               if (val & INT_REG_CED) {
+                       if (val & INT_REG_ERR) {
+                               retval = ms_read_status_reg(chip);
+                               if (retval != STATUS_SUCCESS) {
+                                       uncorrect_flag = 1;
+                                       RTSX_DEBUGP("Uncorrectable error\n");
+                               } else {
+                                       uncorrect_flag = 0;
+                               }
+
+                               retval = ms_transfer_tpc(chip,
+                                                       MS_TM_NORMAL_READ,
+                                                       READ_PAGE_DATA,
+                                                       0, NO_WAIT_INT);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+
+                               if (uncorrect_flag) {
+                                       ms_set_page_status(log_blk, setPS_NG,
+                                                       extra, MS_EXTRA_SIZE);
+                                       if (i == 0)
+                                               extra[0] &= 0xEF;
+
+                                       ms_write_extra_data(chip, old_blk, i,
+                                                       extra, MS_EXTRA_SIZE);
+                                       RTSX_DEBUGP("page %d : extra[0] = 0x%x\n", i, extra[0]);
+                                       MS_SET_BAD_BLOCK_FLG(ms_card);
+
+                                       ms_set_page_status(log_blk, setPS_Error,
+                                                       extra, MS_EXTRA_SIZE);
+                                       ms_write_extra_data(chip, new_blk, i,
+                                                       extra, MS_EXTRA_SIZE);
+                                       continue;
+                               }
+
+                               for (rty_cnt = 0; rty_cnt < MS_MAX_RETRY_COUNT;
+                                    rty_cnt++) {
+                                       retval = ms_transfer_tpc(
+                                               chip,
+                                               MS_TM_NORMAL_WRITE,
+                                               WRITE_PAGE_DATA,
+                                               0, NO_WAIT_INT);
+                                       if (retval == STATUS_SUCCESS)
+                                               break;
+                               }
+                               if (rty_cnt == MS_MAX_RETRY_COUNT)
+                                       TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       if (!(val & INT_REG_BREQ)) {
+                               ms_set_err_code(chip, MS_BREQ_ERROR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+
+               retval = ms_set_rw_reg_addr(chip, OverwriteFlag,
+                               MS_EXTRA_SIZE, SystemParm, (6+MS_EXTRA_SIZE));
+
+               ms_set_err_code(chip, MS_NO_ERROR);
+
+               if (CHK_MS4BIT(ms_card))
+                       data[0] = 0x88;
+               else
+                       data[0] = 0x80;
+
+               data[1] = 0;
+               data[2] = (u8)(new_blk >> 8);
+               data[3] = (u8)new_blk;
+               data[4] = 0x20;
+               data[5] = i;
+
+               if ((extra[0] & 0x60) != 0x60)
+                       data[6] = extra[0];
+               else
+                       data[6] = 0xF8;
+
+               data[6 + 1] = 0xFF;
+               data[6 + 2] = (u8)(log_blk >> 8);
+               data[6 + 3] = (u8)log_blk;
+
+               for (j = 4; j <= MS_EXTRA_SIZE; j++)
+                       data[6 + j] = 0xFF;
+
+               retval = ms_write_bytes(chip, WRITE_REG, (6 + MS_EXTRA_SIZE),
+                                       NO_WAIT_INT, data, 16);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               ms_set_err_code(chip, MS_NO_ERROR);
+               retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if (val & INT_REG_CMDNK) {
+                       ms_set_err_code(chip, MS_CMD_NK);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               if (val & INT_REG_CED) {
+                       if (val & INT_REG_ERR) {
+                               ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+
+               if (i == 0) {
+                       retval = ms_set_rw_reg_addr(chip, OverwriteFlag,
+                                               MS_EXTRA_SIZE, SystemParm, 7);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       ms_set_err_code(chip, MS_NO_ERROR);
+
+                       if (CHK_MS4BIT(ms_card))
+                               data[0] = 0x88;
+                       else
+                               data[0] = 0x80;
+
+                       data[1] = 0;
+                       data[2] = (u8)(old_blk >> 8);
+                       data[3] = (u8)old_blk;
+                       data[4] = 0x80;
+                       data[5] = 0;
+                       data[6] = 0xEF;
+                       data[7] = 0xFF;
+
+                       retval = ms_write_bytes(chip, WRITE_REG, 7,
+                                               NO_WAIT_INT, data, 8);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       ms_set_err_code(chip, MS_NO_ERROR);
+                       retval = ms_read_bytes(chip, GET_INT, 1,
+                                       NO_WAIT_INT, &val, 1);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       if (val & INT_REG_CMDNK) {
+                               ms_set_err_code(chip, MS_CMD_NK);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       if (val & INT_REG_CED) {
+                               if (val & INT_REG_ERR) {
+                                       ms_set_err_code(chip,
+                                                       MS_FLASH_WRITE_ERROR);
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               }
+                       }
+               }
+       }
+
+       return STATUS_SUCCESS;
+}
+
+
+static int reset_ms(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+       u16 i, reg_addr, block_size;
+       u8 val, extra[MS_EXTRA_SIZE], j, *ptr;
+#ifndef SUPPORT_MAGIC_GATE
+       u16 eblock_cnt;
+#endif
+
+       retval = ms_prepare_reset(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ms_card->ms_type |= TYPE_MS;
+
+       retval = ms_send_cmd(chip, MS_RESET, NO_WAIT_INT);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = ms_read_status_reg(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_READ_REG(chip, PPBUF_BASE2, &val);
+       if (val & WRT_PRTCT)
+               chip->card_wp |= MS_CARD;
+       else
+               chip->card_wp &= ~MS_CARD;
+
+       i = 0;
+
+RE_SEARCH:
+       /* Search Boot Block */
+       while (i < (MAX_DEFECTIVE_BLOCK + 2)) {
+               if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+                       ms_set_err_code(chip, MS_NO_CARD);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               retval = ms_read_extra_data(chip, i, 0, extra, MS_EXTRA_SIZE);
+               if (retval != STATUS_SUCCESS) {
+                       i++;
+                       continue;
+               }
+
+               if (extra[0] & BLOCK_OK) {
+                       if (!(extra[1] & NOT_BOOT_BLOCK)) {
+                               ms_card->boot_block = i;
+                               break;
+                       }
+               }
+               i++;
+       }
+
+       if (i == (MAX_DEFECTIVE_BLOCK + 2)) {
+               RTSX_DEBUGP("No boot block found!");
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       for (j = 0; j < 3; j++) {
+               retval = ms_read_page(chip, ms_card->boot_block, j);
+               if (retval != STATUS_SUCCESS) {
+                       if (ms_check_err_code(chip, MS_FLASH_WRITE_ERROR)) {
+                               i = ms_card->boot_block + 1;
+                               ms_set_err_code(chip, MS_NO_ERROR);
+                               goto RE_SEARCH;
+                       }
+               }
+       }
+
+       retval = ms_read_page(chip, ms_card->boot_block, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       /* Read MS system information as sys_info */
+       rtsx_init_cmd(chip);
+
+       for (i = 0; i < 96; i++)
+               rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 0x1A0 + i, 0, 0);
+
+       retval = rtsx_send_cmd(chip, MS_CARD, 100);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ptr = rtsx_get_cmd_data(chip);
+       memcpy(ms_card->raw_sys_info, ptr, 96);
+
+       /* Read useful block contents */
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, READ_REG_CMD, HEADER_ID0, 0, 0);
+       rtsx_add_cmd(chip, READ_REG_CMD, HEADER_ID1, 0, 0);
+
+       for (reg_addr = DISABLED_BLOCK0; reg_addr <= DISABLED_BLOCK3;
+            reg_addr++)
+               rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
+
+       for (reg_addr = BLOCK_SIZE_0; reg_addr <= PAGE_SIZE_1; reg_addr++)
+               rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
+
+       rtsx_add_cmd(chip, READ_REG_CMD, MS_Device_Type, 0, 0);
+       rtsx_add_cmd(chip, READ_REG_CMD, MS_4bit_Support, 0, 0);
+
+       retval = rtsx_send_cmd(chip, MS_CARD, 100);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ptr = rtsx_get_cmd_data(chip);
+
+       RTSX_DEBUGP("Boot block data:\n");
+       RTSX_DUMP(ptr, 16);
+
+       /* Block ID error
+        * HEADER_ID0, HEADER_ID1
+        */
+       if (ptr[0] != 0x00 || ptr[1] != 0x01) {
+               i = ms_card->boot_block + 1;
+               goto RE_SEARCH;
+       }
+
+       /* Page size error
+        * PAGE_SIZE_0, PAGE_SIZE_1
+        */
+       if (ptr[12] != 0x02 || ptr[13] != 0x00) {
+               i = ms_card->boot_block + 1;
+               goto RE_SEARCH;
+       }
+
+       if ((ptr[14] == 1) || (ptr[14] == 3))
+               chip->card_wp |= MS_CARD;
+
+       /* BLOCK_SIZE_0, BLOCK_SIZE_1 */
+       block_size = ((u16)ptr[6] << 8) | ptr[7];
+       if (block_size == 0x0010) {
+               /* Block size 16KB */
+               ms_card->block_shift = 5;
+               ms_card->page_off = 0x1F;
+       } else if (block_size == 0x0008) {
+               /* Block size 8KB */
+               ms_card->block_shift = 4;
+               ms_card->page_off = 0x0F;
+       }
+
+       /* BLOCK_COUNT_0, BLOCK_COUNT_1 */
+       ms_card->total_block = ((u16)ptr[8] << 8) | ptr[9];
+
+#ifdef SUPPORT_MAGIC_GATE
+       j = ptr[10];
+
+       if (ms_card->block_shift == 4)  { /* 4MB or 8MB */
+               if (j < 2)  { /* Effective block for 4MB: 0x1F0 */
+                       ms_card->capacity = 0x1EE0;
+               } else { /* Effective block for 8MB: 0x3E0 */
+                       ms_card->capacity = 0x3DE0;
+               }
+       } else  { /* 16MB, 32MB, 64MB or 128MB */
+               if (j < 5)  { /* Effective block for 16MB: 0x3E0 */
+                       ms_card->capacity = 0x7BC0;
+               } else if (j < 0xA) { /* Effective block for 32MB: 0x7C0 */
+                       ms_card->capacity = 0xF7C0;
+               } else if (j < 0x11) { /* Effective block for 64MB: 0xF80 */
+                       ms_card->capacity = 0x1EF80;
+               } else { /* Effective block for 128MB: 0x1F00 */
+                       ms_card->capacity = 0x3DF00;
+               }
+       }
+#else
+       /* EBLOCK_COUNT_0, EBLOCK_COUNT_1 */
+       eblock_cnt = ((u16)ptr[10] << 8) | ptr[11];
+
+       ms_card->capacity = ((u32)eblock_cnt - 2) << ms_card->block_shift;
+#endif
+
+       chip->capacity[chip->card2lun[MS_CARD]] = ms_card->capacity;
+
+       /* Switch I/F Mode */
+       if (ptr[15]) {
+               retval = ms_set_rw_reg_addr(chip, 0, 0, SystemParm, 1);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               RTSX_WRITE_REG(chip, PPBUF_BASE2, 0xFF, 0x88);
+               RTSX_WRITE_REG(chip, PPBUF_BASE2 + 1, 0xFF, 0);
+
+               retval = ms_transfer_tpc(chip, MS_TM_WRITE_BYTES, WRITE_REG , 1,
+                                       NO_WAIT_INT);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               RTSX_WRITE_REG(chip, MS_CFG, 0x58 | MS_NO_CHECK_INT,
+                       MS_BUS_WIDTH_4 | PUSH_TIME_ODD | MS_NO_CHECK_INT);
+
+               ms_card->ms_type |= MS_4BIT;
+       }
+
+       if (CHK_MS4BIT(ms_card))
+               chip->card_bus_width[chip->card2lun[MS_CARD]] = 4;
+       else
+               chip->card_bus_width[chip->card2lun[MS_CARD]] = 1;
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_init_l2p_tbl(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int size, i, seg_no, retval;
+       u16 defect_block, reg_addr;
+       u8 val1, val2;
+
+       ms_card->segment_cnt = ms_card->total_block >> 9;
+       RTSX_DEBUGP("ms_card->segment_cnt = %d\n", ms_card->segment_cnt);
+
+       size = ms_card->segment_cnt * sizeof(struct zone_entry);
+       ms_card->segment = vzalloc(size);
+       if (ms_card->segment == NULL)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = ms_read_page(chip, ms_card->boot_block, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_GOTO(chip, INIT_FAIL);
+
+       reg_addr = PPBUF_BASE2;
+       for (i = 0; i < (((ms_card->total_block >> 9) * 10) + 1); i++) {
+               retval = rtsx_read_register(chip, reg_addr++, &val1);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, INIT_FAIL);
+
+               retval = rtsx_read_register(chip, reg_addr++, &val2);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, INIT_FAIL);
+
+               defect_block = ((u16)val1 << 8) | val2;
+               if (defect_block == 0xFFFF)
+                       break;
+
+               seg_no = defect_block / 512;
+               ms_card->segment[seg_no].defect_list[ms_card->segment[seg_no].disable_count++] = defect_block;
+       }
+
+       for (i = 0; i < ms_card->segment_cnt; i++) {
+               ms_card->segment[i].build_flag = 0;
+               ms_card->segment[i].l2p_table = NULL;
+               ms_card->segment[i].free_table = NULL;
+               ms_card->segment[i].get_index = 0;
+               ms_card->segment[i].set_index = 0;
+               ms_card->segment[i].unused_blk_cnt = 0;
+
+               RTSX_DEBUGP("defective block count of segment %d is %d\n",
+                                       i, ms_card->segment[i].disable_count);
+       }
+
+       return STATUS_SUCCESS;
+
+INIT_FAIL:
+       if (ms_card->segment) {
+               vfree(ms_card->segment);
+               ms_card->segment = NULL;
+       }
+
+       return STATUS_FAIL;
+}
+
+static u16 ms_get_l2p_tbl(struct rtsx_chip *chip, int seg_no, u16 log_off)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       struct zone_entry *segment;
+
+       if (ms_card->segment == NULL)
+               return 0xFFFF;
+
+       segment = &(ms_card->segment[seg_no]);
+
+       if (segment->l2p_table)
+               return segment->l2p_table[log_off];
+
+       return 0xFFFF;
+}
+
+static void ms_set_l2p_tbl(struct rtsx_chip *chip,
+                       int seg_no, u16 log_off, u16 phy_blk)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       struct zone_entry *segment;
+
+       if (ms_card->segment == NULL)
+               return;
+
+       segment = &(ms_card->segment[seg_no]);
+       if (segment->l2p_table)
+               segment->l2p_table[log_off] = phy_blk;
+}
+
+static void ms_set_unused_block(struct rtsx_chip *chip, u16 phy_blk)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       struct zone_entry *segment;
+       int seg_no;
+
+       seg_no = (int)phy_blk >> 9;
+       segment = &(ms_card->segment[seg_no]);
+
+       segment->free_table[segment->set_index++] = phy_blk;
+       if (segment->set_index >= MS_FREE_TABLE_CNT)
+               segment->set_index = 0;
+
+       segment->unused_blk_cnt++;
+}
+
+static u16 ms_get_unused_block(struct rtsx_chip *chip, int seg_no)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       struct zone_entry *segment;
+       u16 phy_blk;
+
+       segment = &(ms_card->segment[seg_no]);
+
+       if (segment->unused_blk_cnt <= 0)
+               return 0xFFFF;
+
+       phy_blk = segment->free_table[segment->get_index];
+       segment->free_table[segment->get_index++] = 0xFFFF;
+       if (segment->get_index >= MS_FREE_TABLE_CNT)
+               segment->get_index = 0;
+
+       segment->unused_blk_cnt--;
+
+       return phy_blk;
+}
+
+static const unsigned short ms_start_idx[] = {0, 494, 990, 1486, 1982, 2478,
+                                             2974, 3470, 3966, 4462, 4958,
+                                             5454, 5950, 6446, 6942, 7438,
+                                             7934};
+
+static int ms_arbitrate_l2p(struct rtsx_chip *chip, u16 phy_blk,
+                       u16 log_off, u8 us1, u8 us2)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       struct zone_entry *segment;
+       int seg_no;
+       u16 tmp_blk;
+
+       seg_no = (int)phy_blk >> 9;
+       segment = &(ms_card->segment[seg_no]);
+       tmp_blk = segment->l2p_table[log_off];
+
+       if (us1 != us2) {
+               if (us1 == 0) {
+                       if (!(chip->card_wp & MS_CARD))
+                               ms_erase_block(chip, tmp_blk);
+
+                       ms_set_unused_block(chip, tmp_blk);
+                       segment->l2p_table[log_off] = phy_blk;
+               } else {
+                       if (!(chip->card_wp & MS_CARD))
+                               ms_erase_block(chip, phy_blk);
+
+                       ms_set_unused_block(chip, phy_blk);
+               }
+       } else {
+               if (phy_blk < tmp_blk) {
+                       if (!(chip->card_wp & MS_CARD))
+                               ms_erase_block(chip, phy_blk);
+
+                       ms_set_unused_block(chip, phy_blk);
+               } else {
+                       if (!(chip->card_wp & MS_CARD))
+                               ms_erase_block(chip, tmp_blk);
+
+                       ms_set_unused_block(chip, tmp_blk);
+                       segment->l2p_table[log_off] = phy_blk;
+               }
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_build_l2p_tbl(struct rtsx_chip *chip, int seg_no)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       struct zone_entry *segment;
+       int retval, table_size, disable_cnt, defect_flag, i;
+       u16 start, end, phy_blk, log_blk, tmp_blk;
+       u8 extra[MS_EXTRA_SIZE], us1, us2;
+
+       RTSX_DEBUGP("ms_build_l2p_tbl: %d\n", seg_no);
+
+       if (ms_card->segment == NULL) {
+               retval = ms_init_l2p_tbl(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, retval);
+       }
+
+       if (ms_card->segment[seg_no].build_flag) {
+               RTSX_DEBUGP("l2p table of segment %d has been built\n", seg_no);
+               return STATUS_SUCCESS;
+       }
+
+       if (seg_no == 0)
+               table_size = 494;
+       else
+               table_size = 496;
+
+       segment = &(ms_card->segment[seg_no]);
+
+       if (segment->l2p_table == NULL) {
+               segment->l2p_table = vmalloc(table_size * 2);
+               if (segment->l2p_table == NULL)
+                       TRACE_GOTO(chip, BUILD_FAIL);
+       }
+       memset((u8 *)(segment->l2p_table), 0xff, table_size * 2);
+
+       if (segment->free_table == NULL) {
+               segment->free_table = vmalloc(MS_FREE_TABLE_CNT * 2);
+               if (segment->free_table == NULL)
+                       TRACE_GOTO(chip, BUILD_FAIL);
+       }
+       memset((u8 *)(segment->free_table), 0xff, MS_FREE_TABLE_CNT * 2);
+
+       start = (u16)seg_no << 9;
+       end = (u16)(seg_no + 1) << 9;
+
+       disable_cnt = segment->disable_count;
+
+       segment->get_index = segment->set_index = 0;
+       segment->unused_blk_cnt = 0;
+
+       for (phy_blk = start; phy_blk < end; phy_blk++) {
+               if (disable_cnt) {
+                       defect_flag = 0;
+                       for (i = 0; i < segment->disable_count; i++) {
+                               if (phy_blk == segment->defect_list[i]) {
+                                       defect_flag = 1;
+                                       break;
+                               }
+                       }
+                       if (defect_flag) {
+                               disable_cnt--;
+                               continue;
+                       }
+               }
+
+               retval = ms_read_extra_data(chip, phy_blk, 0,
+                                       extra, MS_EXTRA_SIZE);
+               if (retval != STATUS_SUCCESS) {
+                       RTSX_DEBUGP("read extra data fail\n");
+                       ms_set_bad_block(chip, phy_blk);
+                       continue;
+               }
+
+               if (seg_no == ms_card->segment_cnt - 1) {
+                       if (!(extra[1] & NOT_TRANSLATION_TABLE)) {
+                               if (!(chip->card_wp & MS_CARD)) {
+                                       retval = ms_erase_block(chip, phy_blk);
+                                       if (retval != STATUS_SUCCESS)
+                                               continue;
+                                       extra[2] = 0xff;
+                                       extra[3] = 0xff;
+                               }
+                       }
+               }
+
+               if (!(extra[0] & BLOCK_OK))
+                       continue;
+               if (!(extra[1] & NOT_BOOT_BLOCK))
+                       continue;
+               if ((extra[0] & PAGE_OK) != PAGE_OK)
+                       continue;
+
+               log_blk = ((u16)extra[2] << 8) | extra[3];
+
+               if (log_blk == 0xFFFF) {
+                       if (!(chip->card_wp & MS_CARD)) {
+                               retval = ms_erase_block(chip, phy_blk);
+                               if (retval != STATUS_SUCCESS)
+                                       continue;
+                       }
+                       ms_set_unused_block(chip, phy_blk);
+                       continue;
+               }
+
+               if ((log_blk < ms_start_idx[seg_no]) ||
+                               (log_blk >= ms_start_idx[seg_no+1])) {
+                       if (!(chip->card_wp & MS_CARD)) {
+                               retval = ms_erase_block(chip, phy_blk);
+                               if (retval != STATUS_SUCCESS)
+                                       continue;
+                       }
+                       ms_set_unused_block(chip, phy_blk);
+                       continue;
+               }
+
+               if (segment->l2p_table[log_blk - ms_start_idx[seg_no]] == 0xFFFF) {
+                       segment->l2p_table[log_blk - ms_start_idx[seg_no]] = phy_blk;
+                       continue;
+               }
+
+               us1 = extra[0] & 0x10;
+               tmp_blk = segment->l2p_table[log_blk - ms_start_idx[seg_no]];
+               retval = ms_read_extra_data(chip, tmp_blk, 0,
+                                       extra, MS_EXTRA_SIZE);
+               if (retval != STATUS_SUCCESS)
+                       continue;
+               us2 = extra[0] & 0x10;
+
+               (void)ms_arbitrate_l2p(chip, phy_blk,
+                               log_blk-ms_start_idx[seg_no], us1, us2);
+               continue;
+       }
+
+       segment->build_flag = 1;
+
+       RTSX_DEBUGP("unused block count: %d\n", segment->unused_blk_cnt);
+
+       /* Logical Address Confirmation Process */
+       if (seg_no == ms_card->segment_cnt - 1) {
+               if (segment->unused_blk_cnt < 2)
+                       chip->card_wp |= MS_CARD;
+       } else {
+               if (segment->unused_blk_cnt < 1)
+                       chip->card_wp |= MS_CARD;
+       }
+
+       if (chip->card_wp & MS_CARD)
+               return STATUS_SUCCESS;
+
+       for (log_blk = ms_start_idx[seg_no];
+            log_blk < ms_start_idx[seg_no + 1]; log_blk++) {
+               if (segment->l2p_table[log_blk-ms_start_idx[seg_no]] == 0xFFFF) {
+                       phy_blk = ms_get_unused_block(chip, seg_no);
+                       if (phy_blk == 0xFFFF) {
+                               chip->card_wp |= MS_CARD;
+                               return STATUS_SUCCESS;
+                       }
+                       retval = ms_init_page(chip, phy_blk, log_blk, 0, 1);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_GOTO(chip, BUILD_FAIL);
+
+                       segment->l2p_table[log_blk-ms_start_idx[seg_no]] = phy_blk;
+                       if (seg_no == ms_card->segment_cnt - 1) {
+                               if (segment->unused_blk_cnt < 2) {
+                                       chip->card_wp |= MS_CARD;
+                                       return STATUS_SUCCESS;
+                               }
+                       } else {
+                               if (segment->unused_blk_cnt < 1) {
+                                       chip->card_wp |= MS_CARD;
+                                       return STATUS_SUCCESS;
+                               }
+                       }
+               }
+       }
+
+       /* Make boot block be the first normal block */
+       if (seg_no == 0) {
+               for (log_blk = 0; log_blk < 494; log_blk++) {
+                       tmp_blk = segment->l2p_table[log_blk];
+                       if (tmp_blk < ms_card->boot_block) {
+                               RTSX_DEBUGP("Boot block is not the first normal block.\n");
+
+                               if (chip->card_wp & MS_CARD)
+                                       break;
+
+                               phy_blk = ms_get_unused_block(chip, 0);
+                               retval = ms_copy_page(chip, tmp_blk, phy_blk,
+                                               log_blk, 0, ms_card->page_off + 1);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+
+                               segment->l2p_table[log_blk] = phy_blk;
+
+                               retval = ms_set_bad_block(chip, tmp_blk);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+       }
+
+       return STATUS_SUCCESS;
+
+BUILD_FAIL:
+       segment->build_flag = 0;
+       if (segment->l2p_table) {
+               vfree(segment->l2p_table);
+               segment->l2p_table = NULL;
+       }
+       if (segment->free_table) {
+               vfree(segment->free_table);
+               segment->free_table = NULL;
+       }
+
+       return STATUS_FAIL;
+}
+
+
+int reset_ms_card(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+
+       memset(ms_card, 0, sizeof(struct ms_info));
+
+       retval = enable_card_clock(chip, MS_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = select_card(chip, MS_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ms_card->ms_type = 0;
+
+       retval = reset_ms_pro(chip);
+       if (retval != STATUS_SUCCESS) {
+               if (ms_card->check_ms_flow) {
+                       retval = reset_ms(chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+               } else {
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       retval = ms_set_init_para(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (!CHK_MSPRO(ms_card)) {
+               /* Build table for the last segment,
+                * to check if L2P table block exists, erasing it
+                */
+               retval = ms_build_l2p_tbl(chip, ms_card->total_block / 512 - 1);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       RTSX_DEBUGP("ms_card->ms_type = 0x%x\n", ms_card->ms_type);
+
+       return STATUS_SUCCESS;
+}
+
+static int mspro_set_rw_cmd(struct rtsx_chip *chip,
+                       u32 start_sec, u16 sec_cnt, u8 cmd)
+{
+       int retval, i;
+       u8 data[8];
+
+       data[0] = cmd;
+       data[1] = (u8)(sec_cnt >> 8);
+       data[2] = (u8)sec_cnt;
+       data[3] = (u8)(start_sec >> 24);
+       data[4] = (u8)(start_sec >> 16);
+       data[5] = (u8)(start_sec >> 8);
+       data[6] = (u8)start_sec;
+       data[7] = 0;
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_write_bytes(chip, PRO_EX_SET_CMD, 7,
+                                       WAIT_INT, data, 8);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (i == MS_MAX_RETRY_COUNT)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+
+void mspro_stop_seq_mode(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       if (ms_card->seq_mode) {
+               retval = ms_switch_clock(chip);
+               if (retval != STATUS_SUCCESS)
+                       return;
+
+               ms_card->seq_mode = 0;
+               ms_card->total_sec_cnt = 0;
+               ms_send_cmd(chip, PRO_STOP, WAIT_INT);
+
+               rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
+       }
+}
+
+static inline int ms_auto_tune_clock(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       if (chip->asic_code) {
+               if (ms_card->ms_clock > 30)
+                       ms_card->ms_clock -= 20;
+       } else {
+               if (ms_card->ms_clock == CLK_80)
+                       ms_card->ms_clock = CLK_60;
+               else if (ms_card->ms_clock == CLK_60)
+                       ms_card->ms_clock = CLK_40;
+       }
+
+       retval = ms_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int mspro_rw_multi_sector(struct scsi_cmnd *srb,
+                               struct rtsx_chip *chip, u32 start_sector,
+                               u16 sector_cnt)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, mode_2k = 0;
+       u16 count;
+       u8 val, trans_mode, rw_tpc, rw_cmd;
+
+       ms_set_err_code(chip, MS_NO_ERROR);
+
+       ms_card->cleanup_counter = 0;
+
+       if (CHK_MSHG(ms_card)) {
+               if ((start_sector % 4) || (sector_cnt % 4)) {
+                       if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+                               rw_tpc = PRO_READ_LONG_DATA;
+                               rw_cmd = PRO_READ_DATA;
+                       } else {
+                               rw_tpc = PRO_WRITE_LONG_DATA;
+                               rw_cmd = PRO_WRITE_DATA;
+                       }
+               } else {
+                       if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+                               rw_tpc = PRO_READ_QUAD_DATA;
+                               rw_cmd = PRO_READ_2K_DATA;
+                       } else {
+                               rw_tpc = PRO_WRITE_QUAD_DATA;
+                               rw_cmd = PRO_WRITE_2K_DATA;
+                       }
+                       mode_2k = 1;
+               }
+       } else {
+               if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+                       rw_tpc = PRO_READ_LONG_DATA;
+                       rw_cmd = PRO_READ_DATA;
+               } else {
+                       rw_tpc = PRO_WRITE_LONG_DATA;
+                       rw_cmd = PRO_WRITE_DATA;
+               }
+       }
+
+       retval = ms_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (srb->sc_data_direction == DMA_FROM_DEVICE)
+               trans_mode = MS_TM_AUTO_READ;
+       else
+               trans_mode = MS_TM_AUTO_WRITE;
+
+       RTSX_READ_REG(chip, MS_TRANS_CFG, &val);
+
+       if (ms_card->seq_mode) {
+               if ((ms_card->pre_dir != srb->sc_data_direction)
+                               || ((ms_card->pre_sec_addr + ms_card->pre_sec_cnt) != start_sector)
+                               || (mode_2k && (ms_card->seq_mode & MODE_512_SEQ))
+                               || (!mode_2k && (ms_card->seq_mode & MODE_2K_SEQ))
+                               || !(val & MS_INT_BREQ)
+                               || ((ms_card->total_sec_cnt + sector_cnt) > 0xFE00)) {
+                       ms_card->seq_mode = 0;
+                       ms_card->total_sec_cnt = 0;
+                       if (val & MS_INT_BREQ) {
+                               retval = ms_send_cmd(chip, PRO_STOP, WAIT_INT);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+
+                               rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
+                       }
+               }
+       }
+
+       if (!ms_card->seq_mode) {
+               ms_card->total_sec_cnt = 0;
+               if (sector_cnt >= SEQ_START_CRITERIA) {
+                       if ((ms_card->capacity - start_sector) > 0xFE00)
+                               count = 0xFE00;
+                       else
+                               count = (u16)(ms_card->capacity - start_sector);
+
+                       if (count > sector_cnt) {
+                               if (mode_2k)
+                                       ms_card->seq_mode |= MODE_2K_SEQ;
+                               else
+                                       ms_card->seq_mode |= MODE_512_SEQ;
+                       }
+               } else {
+                       count = sector_cnt;
+               }
+               retval = mspro_set_rw_cmd(chip, start_sector, count, rw_cmd);
+               if (retval != STATUS_SUCCESS) {
+                       ms_card->seq_mode = 0;
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       retval = ms_transfer_data(chip, trans_mode, rw_tpc, sector_cnt,
+                               WAIT_INT, mode_2k, scsi_sg_count(srb),
+                               scsi_sglist(srb), scsi_bufflen(srb));
+       if (retval != STATUS_SUCCESS) {
+               ms_card->seq_mode = 0;
+               rtsx_read_register(chip, MS_TRANS_CFG, &val);
+               rtsx_clear_ms_error(chip);
+
+               if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+                       chip->rw_need_retry = 0;
+                       RTSX_DEBUGP("No card exist, exit mspro_rw_multi_sector\n");
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               if (val & MS_INT_BREQ)
+                       ms_send_cmd(chip, PRO_STOP, WAIT_INT);
+
+               if (val & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) {
+                       RTSX_DEBUGP("MSPro CRC error, tune clock!\n");
+                       chip->rw_need_retry = 1;
+                       ms_auto_tune_clock(chip);
+               }
+
+               TRACE_RET(chip, retval);
+       }
+
+       if (ms_card->seq_mode) {
+               ms_card->pre_sec_addr = start_sector;
+               ms_card->pre_sec_cnt = sector_cnt;
+               ms_card->pre_dir = srb->sc_data_direction;
+               ms_card->total_sec_cnt += sector_cnt;
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int mspro_read_format_progress(struct rtsx_chip *chip,
+                               const int short_data_len)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i;
+       u32 total_progress, cur_progress;
+       u8 cnt, tmp;
+       u8 data[8];
+
+       RTSX_DEBUGP("mspro_read_format_progress, short_data_len = %d\n",
+               short_data_len);
+
+       retval = ms_switch_clock(chip);
+       if (retval != STATUS_SUCCESS) {
+               ms_card->format_status = FORMAT_FAIL;
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = rtsx_read_register(chip, MS_TRANS_CFG, &tmp);
+       if (retval != STATUS_SUCCESS) {
+               ms_card->format_status = FORMAT_FAIL;
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (!(tmp & MS_INT_BREQ)) {
+               if ((tmp & (MS_INT_CED | MS_INT_BREQ | MS_INT_CMDNK | MS_INT_ERR)) == MS_INT_CED) {
+                       ms_card->format_status = FORMAT_SUCCESS;
+                       return STATUS_SUCCESS;
+               }
+               ms_card->format_status = FORMAT_FAIL;
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (short_data_len >= 256)
+               cnt = 0;
+       else
+               cnt = (u8)short_data_len;
+
+       retval = rtsx_write_register(chip, MS_CFG, MS_NO_CHECK_INT,
+                               MS_NO_CHECK_INT);
+       if (retval != STATUS_SUCCESS) {
+               ms_card->format_status = FORMAT_FAIL;
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = ms_read_bytes(chip, PRO_READ_SHORT_DATA, cnt, WAIT_INT,
+                       data, 8);
+       if (retval != STATUS_SUCCESS) {
+               ms_card->format_status = FORMAT_FAIL;
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       total_progress = (data[0] << 24) | (data[1] << 16) |
+               (data[2] << 8) | data[3];
+       cur_progress = (data[4] << 24) | (data[5] << 16) |
+               (data[6] << 8) | data[7];
+
+       RTSX_DEBUGP("total_progress = %d, cur_progress = %d\n",
+                               total_progress, cur_progress);
+
+       if (total_progress == 0) {
+               ms_card->progress = 0;
+       } else {
+               u64 ulltmp = (u64)cur_progress * (u64)65535;
+               do_div(ulltmp, total_progress);
+               ms_card->progress = (u16)ulltmp;
+       }
+       RTSX_DEBUGP("progress = %d\n", ms_card->progress);
+
+       for (i = 0; i < 5000; i++) {
+               retval = rtsx_read_register(chip, MS_TRANS_CFG, &tmp);
+               if (retval != STATUS_SUCCESS) {
+                       ms_card->format_status = FORMAT_FAIL;
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+               if (tmp & (MS_INT_CED | MS_INT_CMDNK |
+                               MS_INT_BREQ | MS_INT_ERR))
+                       break;
+
+               wait_timeout(1);
+       }
+
+       retval = rtsx_write_register(chip, MS_CFG, MS_NO_CHECK_INT, 0);
+       if (retval != STATUS_SUCCESS) {
+               ms_card->format_status = FORMAT_FAIL;
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (i == 5000) {
+               ms_card->format_status = FORMAT_FAIL;
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (tmp & (MS_INT_CMDNK | MS_INT_ERR)) {
+               ms_card->format_status = FORMAT_FAIL;
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (tmp & MS_INT_CED) {
+               ms_card->format_status = FORMAT_SUCCESS;
+               ms_card->pro_under_formatting = 0;
+       } else if (tmp & MS_INT_BREQ) {
+               ms_card->format_status = FORMAT_IN_PROGRESS;
+       } else {
+               ms_card->format_status = FORMAT_FAIL;
+               ms_card->pro_under_formatting = 0;
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+void mspro_polling_format_status(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int i;
+
+       if (ms_card->pro_under_formatting &&
+               (rtsx_get_stat(chip) != RTSX_STAT_SS)) {
+               rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+               for (i = 0; i < 65535; i++) {
+                       mspro_read_format_progress(chip, MS_SHORT_DATA_LEN);
+                       if (ms_card->format_status != FORMAT_IN_PROGRESS)
+                               break;
+               }
+       }
+
+       return;
+}
+
+int mspro_format(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+               int short_data_len, int quick_format)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i;
+       u8 buf[8], tmp;
+       u16 para;
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       retval = ms_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = ms_set_rw_reg_addr(chip, 0x00, 0x00, Pro_TPCParm, 0x01);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       memset(buf, 0, 2);
+       switch (short_data_len) {
+       case 32:
+               buf[0] = 0;
+               break;
+       case 64:
+               buf[0] = 1;
+               break;
+       case 128:
+               buf[0] = 2;
+               break;
+       case 256:
+       default:
+               buf[0] = 3;
+               break;
+       }
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_write_bytes(chip, PRO_WRITE_REG, 1,
+                                       NO_WAIT_INT, buf, 2);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (i == MS_MAX_RETRY_COUNT)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (quick_format)
+               para = 0x0000;
+       else
+               para = 0x0001;
+
+       retval = mspro_set_rw_cmd(chip, 0, para, PRO_FORMAT);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_READ_REG(chip, MS_TRANS_CFG, &tmp);
+
+       if (tmp & (MS_INT_CMDNK | MS_INT_ERR))
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if ((tmp & (MS_INT_BREQ | MS_INT_CED)) == MS_INT_BREQ) {
+               ms_card->pro_under_formatting = 1;
+               ms_card->progress = 0;
+               ms_card->format_status = FORMAT_IN_PROGRESS;
+               return STATUS_SUCCESS;
+       }
+
+       if (tmp & MS_INT_CED) {
+               ms_card->pro_under_formatting = 0;
+               ms_card->progress = 0;
+               ms_card->format_status = FORMAT_SUCCESS;
+               set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_NO_SENSE);
+               return STATUS_SUCCESS;
+       }
+
+       TRACE_RET(chip, STATUS_FAIL);
+}
+
+
+static int ms_read_multiple_pages(struct rtsx_chip *chip, u16 phy_blk,
+                               u16 log_blk, u8 start_page, u8 end_page,
+                               u8 *buf, unsigned int *index,
+                               unsigned int *offset)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i;
+       u8 extra[MS_EXTRA_SIZE], page_addr, val, trans_cfg, data[6];
+       u8 *ptr;
+
+       retval = ms_read_extra_data(chip, phy_blk, start_page,
+                               extra, MS_EXTRA_SIZE);
+       if (retval == STATUS_SUCCESS) {
+               if ((extra[1] & 0x30) != 0x30) {
+                       ms_set_err_code(chip, MS_FLASH_READ_ERROR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+                               SystemParm, 6);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (CHK_MS4BIT(ms_card))
+               data[0] = 0x88;
+       else
+               data[0] = 0x80;
+
+       data[1] = 0;
+       data[2] = (u8)(phy_blk >> 8);
+       data[3] = (u8)phy_blk;
+       data[4] = 0;
+       data[5] = start_page;
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT,
+                                       data, 6);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (i == MS_MAX_RETRY_COUNT)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ms_set_err_code(chip, MS_NO_ERROR);
+
+       retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ptr = buf;
+
+       for (page_addr = start_page; page_addr < end_page; page_addr++) {
+               ms_set_err_code(chip, MS_NO_ERROR);
+
+               if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+                       ms_set_err_code(chip, MS_NO_CARD);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if (val & INT_REG_CMDNK) {
+                       ms_set_err_code(chip, MS_CMD_NK);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+               if (val & INT_REG_ERR) {
+                       if (val & INT_REG_BREQ) {
+                               retval = ms_read_status_reg(chip);
+                               if (retval != STATUS_SUCCESS) {
+                                       if (!(chip->card_wp & MS_CARD)) {
+                                               reset_ms(chip);
+                                               ms_set_page_status(log_blk, setPS_NG, extra, MS_EXTRA_SIZE);
+                                               ms_write_extra_data(chip, phy_blk,
+                                                               page_addr, extra, MS_EXTRA_SIZE);
+                                       }
+                                       ms_set_err_code(chip, MS_FLASH_READ_ERROR);
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               }
+                       } else {
+                               ms_set_err_code(chip, MS_FLASH_READ_ERROR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               } else {
+                       if (!(val & INT_REG_BREQ)) {
+                               ms_set_err_code(chip, MS_BREQ_ERROR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+
+               if (page_addr == (end_page - 1)) {
+                       if (!(val & INT_REG_CED)) {
+                               retval = ms_send_cmd(chip, BLOCK_END, WAIT_INT);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT,
+                                       &val, 1);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       if (!(val & INT_REG_CED)) {
+                               ms_set_err_code(chip, MS_FLASH_READ_ERROR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       trans_cfg = NO_WAIT_INT;
+               } else {
+                       trans_cfg = WAIT_INT;
+               }
+
+               rtsx_init_cmd(chip);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, READ_PAGE_DATA);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG,
+                       0xFF, trans_cfg);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, RING_BUFFER);
+
+               trans_dma_enable(DMA_FROM_DEVICE, chip, 512, DMA_512);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF,
+                               MS_TRANSFER_START |  MS_TM_NORMAL_READ);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER,
+                       MS_TRANSFER_END, MS_TRANSFER_END);
+
+               rtsx_send_cmd_no_wait(chip);
+
+               retval = rtsx_transfer_data_partial(chip, MS_CARD, ptr,
+                                               512, scsi_sg_count(chip->srb),
+                                               index, offset, DMA_FROM_DEVICE,
+                                               chip->ms_timeout);
+               if (retval < 0) {
+                       if (retval == -ETIMEDOUT) {
+                               ms_set_err_code(chip, MS_TO_ERROR);
+                               rtsx_clear_ms_error(chip);
+                               TRACE_RET(chip, STATUS_TIMEDOUT);
+                       }
+
+                       retval = rtsx_read_register(chip, MS_TRANS_CFG, &val);
+                       if (retval != STATUS_SUCCESS) {
+                               ms_set_err_code(chip, MS_TO_ERROR);
+                               rtsx_clear_ms_error(chip);
+                               TRACE_RET(chip, STATUS_TIMEDOUT);
+                       }
+                       if (val & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) {
+                               ms_set_err_code(chip, MS_CRC16_ERROR);
+                               rtsx_clear_ms_error(chip);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+
+               if (scsi_sg_count(chip->srb) == 0)
+                       ptr += 512;
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_write_multiple_pages(struct rtsx_chip *chip, u16 old_blk,
+                               u16 new_blk, u16 log_blk, u8 start_page,
+                               u8 end_page, u8 *buf, unsigned int *index,
+                               unsigned int *offset)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, i;
+       u8 page_addr, val, data[16];
+       u8 *ptr;
+
+       if (!start_page) {
+               retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+                                       SystemParm, 7);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if (CHK_MS4BIT(ms_card))
+                       data[0] = 0x88;
+               else
+                       data[0] = 0x80;
+
+               data[1] = 0;
+               data[2] = (u8)(old_blk >> 8);
+               data[3] = (u8)old_blk;
+               data[4] = 0x80;
+               data[5] = 0;
+               data[6] = 0xEF;
+               data[7] = 0xFF;
+
+               retval = ms_write_bytes(chip, WRITE_REG, 7, NO_WAIT_INT,
+                                       data, 8);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               ms_set_err_code(chip, MS_NO_ERROR);
+               retval = ms_transfer_tpc(chip, MS_TM_READ_BYTES, GET_INT, 1,
+                                       NO_WAIT_INT);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+                               SystemParm, (6 + MS_EXTRA_SIZE));
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ms_set_err_code(chip, MS_NO_ERROR);
+
+       if (CHK_MS4BIT(ms_card))
+               data[0] = 0x88;
+       else
+               data[0] = 0x80;
+
+       data[1] = 0;
+       data[2] = (u8)(new_blk >> 8);
+       data[3] = (u8)new_blk;
+       if ((end_page - start_page) == 1)
+               data[4] = 0x20;
+       else
+               data[4] = 0;
+
+       data[5] = start_page;
+       data[6] = 0xF8;
+       data[7] = 0xFF;
+       data[8] = (u8)(log_blk >> 8);
+       data[9] = (u8)log_blk;
+
+       for (i = 0x0A; i < 0x10; i++)
+               data[i] = 0xFF;
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_write_bytes(chip, WRITE_REG, 6 + MS_EXTRA_SIZE,
+                                       NO_WAIT_INT, data, 16);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (i == MS_MAX_RETRY_COUNT)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (i == MS_MAX_RETRY_COUNT)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ptr = buf;
+       for (page_addr = start_page; page_addr < end_page; page_addr++) {
+               ms_set_err_code(chip, MS_NO_ERROR);
+
+               if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+                       ms_set_err_code(chip, MS_NO_CARD);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               if (val & INT_REG_CMDNK) {
+                       ms_set_err_code(chip, MS_CMD_NK);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+               if (val & INT_REG_ERR) {
+                       ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+               if (!(val & INT_REG_BREQ)) {
+                       ms_set_err_code(chip, MS_BREQ_ERROR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               udelay(30);
+
+               rtsx_init_cmd(chip);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC,
+                       0xFF, WRITE_PAGE_DATA);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG,
+                       0xFF, WAIT_INT);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, RING_BUFFER);
+
+               trans_dma_enable(DMA_TO_DEVICE, chip, 512, DMA_512);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF,
+                               MS_TRANSFER_START |  MS_TM_NORMAL_WRITE);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER,
+                       MS_TRANSFER_END, MS_TRANSFER_END);
+
+               rtsx_send_cmd_no_wait(chip);
+
+               retval = rtsx_transfer_data_partial(chip, MS_CARD, ptr,
+                                               512, scsi_sg_count(chip->srb),
+                                               index, offset, DMA_TO_DEVICE,
+                                               chip->ms_timeout);
+               if (retval < 0) {
+                       ms_set_err_code(chip, MS_TO_ERROR);
+                       rtsx_clear_ms_error(chip);
+
+                       if (retval == -ETIMEDOUT)
+                               TRACE_RET(chip, STATUS_TIMEDOUT);
+                       else
+                               TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if ((end_page - start_page) == 1) {
+                       if (!(val & INT_REG_CED)) {
+                               ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               } else {
+                       if (page_addr == (end_page - 1)) {
+                               if (!(val & INT_REG_CED)) {
+                                       retval = ms_send_cmd(chip, BLOCK_END,
+                                                       WAIT_INT);
+                                       if (retval != STATUS_SUCCESS)
+                                               TRACE_RET(chip, STATUS_FAIL);
+                               }
+
+                               retval = ms_read_bytes(chip, GET_INT, 1,
+                                               NO_WAIT_INT, &val, 1);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       if ((page_addr == (end_page - 1)) ||
+                               (page_addr == ms_card->page_off)) {
+                               if (!(val & INT_REG_CED)) {
+                                       ms_set_err_code(chip,
+                                                       MS_FLASH_WRITE_ERROR);
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               }
+                       }
+               }
+
+               if (scsi_sg_count(chip->srb) == 0)
+                       ptr += 512;
+       }
+
+       return STATUS_SUCCESS;
+}
+
+
+static int ms_finish_write(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
+               u16 log_blk, u8 page_off)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval, seg_no;
+
+       retval = ms_copy_page(chip, old_blk, new_blk, log_blk,
+                       page_off, ms_card->page_off + 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       seg_no = old_blk >> 9;
+
+       if (MS_TST_BAD_BLOCK_FLG(ms_card)) {
+               MS_CLR_BAD_BLOCK_FLG(ms_card);
+               ms_set_bad_block(chip, old_blk);
+       } else {
+               retval = ms_erase_block(chip, old_blk);
+               if (retval == STATUS_SUCCESS)
+                       ms_set_unused_block(chip, old_blk);
+       }
+
+       ms_set_l2p_tbl(chip, seg_no, log_blk - ms_start_idx[seg_no], new_blk);
+
+       return STATUS_SUCCESS;
+}
+
+static int ms_prepare_write(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
+               u16 log_blk, u8 start_page)
+{
+       int retval;
+
+       if (start_page) {
+               retval = ms_copy_page(chip, old_blk, new_blk, log_blk,
+                               0, start_page);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+#ifdef MS_DELAY_WRITE
+int ms_delay_write(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       struct ms_delay_write_tag *delay_write = &(ms_card->delay_write);
+       int retval;
+
+       if (delay_write->delay_write_flag) {
+               retval = ms_set_init_para(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               delay_write->delay_write_flag = 0;
+               retval = ms_finish_write(chip,
+                                       delay_write->old_phyblock,
+                                       delay_write->new_phyblock,
+                                       delay_write->logblock,
+                                       delay_write->pageoff);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+#endif
+
+static inline void ms_rw_fail(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       if (srb->sc_data_direction == DMA_FROM_DEVICE)
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+       else
+               set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+}
+
+static int ms_rw_multi_sector(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+                       u32 start_sector, u16 sector_cnt)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       unsigned int lun = SCSI_LUN(srb);
+       int retval, seg_no;
+       unsigned int index = 0, offset = 0;
+       u16 old_blk = 0, new_blk = 0, log_blk, total_sec_cnt = sector_cnt;
+       u8 start_page, end_page = 0, page_cnt;
+       u8 *ptr;
+#ifdef MS_DELAY_WRITE
+       struct ms_delay_write_tag *delay_write = &(ms_card->delay_write);
+#endif
+
+       ms_set_err_code(chip, MS_NO_ERROR);
+
+       ms_card->cleanup_counter = 0;
+
+       ptr = (u8 *)scsi_sglist(srb);
+
+       retval = ms_switch_clock(chip);
+       if (retval != STATUS_SUCCESS) {
+               ms_rw_fail(srb, chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       log_blk = (u16)(start_sector >> ms_card->block_shift);
+       start_page = (u8)(start_sector & ms_card->page_off);
+
+       for (seg_no = 0; seg_no < ARRAY_SIZE(ms_start_idx) - 1; seg_no++) {
+               if (log_blk < ms_start_idx[seg_no+1])
+                       break;
+       }
+
+       if (ms_card->segment[seg_no].build_flag == 0) {
+               retval = ms_build_l2p_tbl(chip, seg_no);
+               if (retval != STATUS_SUCCESS) {
+                       chip->card_fail |= MS_CARD;
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       if (srb->sc_data_direction == DMA_TO_DEVICE) {
+#ifdef MS_DELAY_WRITE
+               if (delay_write->delay_write_flag &&
+                               (delay_write->logblock == log_blk) &&
+                               (start_page > delay_write->pageoff)) {
+                       delay_write->delay_write_flag = 0;
+                       retval = ms_copy_page(chip,
+                               delay_write->old_phyblock,
+                               delay_write->new_phyblock, log_blk,
+                               delay_write->pageoff, start_page);
+                       if (retval != STATUS_SUCCESS) {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       old_blk = delay_write->old_phyblock;
+                       new_blk = delay_write->new_phyblock;
+               } else if (delay_write->delay_write_flag &&
+                               (delay_write->logblock == log_blk) &&
+                               (start_page == delay_write->pageoff)) {
+                       delay_write->delay_write_flag = 0;
+                       old_blk = delay_write->old_phyblock;
+                       new_blk = delay_write->new_phyblock;
+               } else {
+                       retval = ms_delay_write(chip);
+                       if (retval != STATUS_SUCCESS) {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+#endif
+                       old_blk = ms_get_l2p_tbl(chip, seg_no,
+                                               log_blk - ms_start_idx[seg_no]);
+                       new_blk  = ms_get_unused_block(chip, seg_no);
+                       if ((old_blk == 0xFFFF) || (new_blk == 0xFFFF)) {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       retval = ms_prepare_write(chip, old_blk, new_blk,
+                                               log_blk, start_page);
+                       if (retval != STATUS_SUCCESS) {
+                               if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+                                       set_sense_type(chip, lun,
+                                               SENSE_TYPE_MEDIA_NOT_PRESENT);
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               }
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+#ifdef MS_DELAY_WRITE
+               }
+#endif
+       } else {
+#ifdef MS_DELAY_WRITE
+               retval = ms_delay_write(chip);
+               if (retval != STATUS_SUCCESS) {
+                       if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_NOT_PRESENT);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+#endif
+               old_blk = ms_get_l2p_tbl(chip, seg_no,
+                                       log_blk - ms_start_idx[seg_no]);
+               if (old_blk == 0xFFFF) {
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       RTSX_DEBUGP("seg_no = %d, old_blk = 0x%x, new_blk = 0x%x\n",
+               seg_no, old_blk, new_blk);
+
+       while (total_sec_cnt) {
+               if ((start_page + total_sec_cnt) > (ms_card->page_off + 1))
+                       end_page = ms_card->page_off + 1;
+               else
+                       end_page = start_page + (u8)total_sec_cnt;
+
+               page_cnt = end_page - start_page;
+
+               RTSX_DEBUGP("start_page = %d, end_page = %d, page_cnt = %d\n",
+                               start_page, end_page, page_cnt);
+
+               if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+                       retval = ms_read_multiple_pages(chip,
+                               old_blk, log_blk, start_page, end_page,
+                               ptr, &index, &offset);
+               } else {
+                       retval = ms_write_multiple_pages(chip, old_blk,
+                               new_blk, log_blk, start_page, end_page,
+                               ptr, &index, &offset);
+               }
+
+               if (retval != STATUS_SUCCESS) {
+                       toggle_gpio(chip, 1);
+                       if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_NOT_PRESENT);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       ms_rw_fail(srb, chip);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               if (srb->sc_data_direction == DMA_TO_DEVICE) {
+                       if (end_page == (ms_card->page_off + 1)) {
+                               retval = ms_erase_block(chip, old_blk);
+                               if (retval == STATUS_SUCCESS)
+                                       ms_set_unused_block(chip, old_blk);
+
+                               ms_set_l2p_tbl(chip, seg_no,
+                                       log_blk - ms_start_idx[seg_no],
+                                       new_blk);
+                       }
+               }
+
+               total_sec_cnt -= page_cnt;
+               if (scsi_sg_count(srb) == 0)
+                       ptr += page_cnt * 512;
+
+               if (total_sec_cnt == 0)
+                       break;
+
+               log_blk++;
+
+               for (seg_no = 0; seg_no < ARRAY_SIZE(ms_start_idx) - 1;
+                               seg_no++) {
+                       if (log_blk < ms_start_idx[seg_no+1])
+                               break;
+               }
+
+               if (ms_card->segment[seg_no].build_flag == 0) {
+                       retval = ms_build_l2p_tbl(chip, seg_no);
+                       if (retval != STATUS_SUCCESS) {
+                               chip->card_fail |= MS_CARD;
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_NOT_PRESENT);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+
+               old_blk = ms_get_l2p_tbl(chip, seg_no,
+                                       log_blk - ms_start_idx[seg_no]);
+               if (old_blk == 0xFFFF) {
+                       ms_rw_fail(srb, chip);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               if (srb->sc_data_direction == DMA_TO_DEVICE) {
+                       new_blk = ms_get_unused_block(chip, seg_no);
+                       if (new_blk == 0xFFFF) {
+                               ms_rw_fail(srb, chip);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+
+               RTSX_DEBUGP("seg_no = %d, old_blk = 0x%x, new_blk = 0x%x\n",
+                       seg_no, old_blk, new_blk);
+
+               start_page = 0;
+       }
+
+       if (srb->sc_data_direction == DMA_TO_DEVICE) {
+               if (end_page < (ms_card->page_off + 1)) {
+#ifdef MS_DELAY_WRITE
+                       delay_write->delay_write_flag = 1;
+                       delay_write->old_phyblock = old_blk;
+                       delay_write->new_phyblock = new_blk;
+                       delay_write->logblock = log_blk;
+                       delay_write->pageoff = end_page;
+#else
+                       retval = ms_finish_write(chip, old_blk, new_blk,
+                                               log_blk, end_page);
+                       if (retval != STATUS_SUCCESS) {
+                               if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+                                       set_sense_type(chip, lun,
+                                               SENSE_TYPE_MEDIA_NOT_PRESENT);
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               }
+
+                               ms_rw_fail(srb, chip);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+#endif
+               }
+       }
+
+       scsi_set_resid(srb, 0);
+
+       return STATUS_SUCCESS;
+}
+
+int ms_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+       u32 start_sector, u16 sector_cnt)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+
+       if (CHK_MSPRO(ms_card))
+               retval = mspro_rw_multi_sector(srb, chip, start_sector,
+                                       sector_cnt);
+       else
+               retval = ms_rw_multi_sector(srb, chip, start_sector,
+                                       sector_cnt);
+
+       return retval;
+}
+
+
+void ms_free_l2p_tbl(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int i = 0;
+
+       if (ms_card->segment != NULL) {
+               for (i = 0; i < ms_card->segment_cnt; i++) {
+                       if (ms_card->segment[i].l2p_table != NULL) {
+                               vfree(ms_card->segment[i].l2p_table);
+                               ms_card->segment[i].l2p_table = NULL;
+                       }
+                       if (ms_card->segment[i].free_table != NULL) {
+                               vfree(ms_card->segment[i].free_table);
+                               ms_card->segment[i].free_table = NULL;
+                       }
+               }
+               vfree(ms_card->segment);
+               ms_card->segment = NULL;
+       }
+}
+
+#ifdef SUPPORT_MAGIC_GATE
+
+#ifdef READ_BYTES_WAIT_INT
+static int ms_poll_int(struct rtsx_chip *chip)
+{
+       int retval;
+       u8 val;
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANS_CFG, MS_INT_CED, MS_INT_CED);
+
+       retval = rtsx_send_cmd(chip, MS_CARD, 5000);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       val = *rtsx_get_cmd_data(chip);
+       if (val & MS_INT_ERR)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+#endif
+
+#ifdef MS_SAMPLE_INT_ERR
+static int check_ms_err(struct rtsx_chip *chip)
+{
+       int retval;
+       u8 val;
+
+       retval = rtsx_read_register(chip, MS_TRANSFER, &val);
+       if (retval != STATUS_SUCCESS)
+               return 1;
+       if (val & MS_TRANSFER_ERR)
+               return 1;
+
+       retval = rtsx_read_register(chip, MS_TRANS_CFG, &val);
+       if (retval != STATUS_SUCCESS)
+               return 1;
+
+       if (val & (MS_INT_ERR | MS_INT_CMDNK))
+               return 1;
+
+       return 0;
+}
+#else
+static int check_ms_err(struct rtsx_chip *chip)
+{
+       int retval;
+       u8 val;
+
+       retval = rtsx_read_register(chip, MS_TRANSFER, &val);
+       if (retval != STATUS_SUCCESS)
+               return 1;
+       if (val & MS_TRANSFER_ERR)
+               return 1;
+
+       return 0;
+}
+#endif
+
+static int mg_send_ex_cmd(struct rtsx_chip *chip, u8 cmd, u8 entry_num)
+{
+       int retval, i;
+       u8 data[8];
+
+       data[0] = cmd;
+       data[1] = 0;
+       data[2] = 0;
+       data[3] = 0;
+       data[4] = 0;
+       data[5] = 0;
+       data[6] = entry_num;
+       data[7] = 0;
+
+       for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+               retval = ms_write_bytes(chip, PRO_EX_SET_CMD, 7, WAIT_INT,
+                                       data, 8);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (i == MS_MAX_RETRY_COUNT)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (check_ms_err(chip)) {
+               rtsx_clear_ms_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int mg_set_tpc_para_sub(struct rtsx_chip *chip, int type,
+                       u8 mg_entry_num)
+{
+       int retval;
+       u8 buf[6];
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       if (type == 0)
+               retval = ms_set_rw_reg_addr(chip, 0, 0, Pro_TPCParm, 1);
+       else
+               retval = ms_set_rw_reg_addr(chip, 0, 0, Pro_DataCount1, 6);
+
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       buf[0] = 0;
+       buf[1] = 0;
+       if (type == 1) {
+               buf[2] = 0;
+               buf[3] = 0;
+               buf[4] = 0;
+               buf[5] = mg_entry_num;
+       }
+       retval = ms_write_bytes(chip, PRO_WRITE_REG, (type == 0) ? 1 : 6,
+                               NO_WAIT_INT, buf, 6);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+int mg_set_leaf_id(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval;
+       int i;
+       unsigned int lun = SCSI_LUN(srb);
+       u8 buf1[32], buf2[12];
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       if (scsi_bufflen(srb) < 12) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       ms_cleanup_work(chip);
+
+       retval = ms_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = mg_send_ex_cmd(chip, MG_SET_LID, 0);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       memset(buf1, 0, 32);
+       rtsx_stor_get_xfer_buf(buf2, min_t(int, 12, scsi_bufflen(srb)), srb);
+       for (i = 0; i < 8; i++)
+               buf1[8+i] = buf2[4+i];
+
+       retval = ms_write_bytes(chip, PRO_WRITE_SHORT_DATA, 32, WAIT_INT,
+                               buf1, 32);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+       if (check_ms_err(chip)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
+               rtsx_clear_ms_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int mg_get_local_EKB(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval = STATUS_FAIL;
+       int bufflen;
+       unsigned int lun = SCSI_LUN(srb);
+       u8 *buf = NULL;
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       ms_cleanup_work(chip);
+
+       retval = ms_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       buf = kmalloc(1540, GFP_KERNEL);
+       if (!buf)
+               TRACE_RET(chip, STATUS_ERROR);
+
+       buf[0] = 0x04;
+       buf[1] = 0x1A;
+       buf[2] = 0x00;
+       buf[3] = 0x00;
+
+       retval = mg_send_ex_cmd(chip, MG_GET_LEKB, 0);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+               TRACE_GOTO(chip, GetEKBFinish);
+       }
+
+       retval = ms_transfer_data(chip, MS_TM_AUTO_READ, PRO_READ_LONG_DATA,
+                               3, WAIT_INT, 0, 0, buf + 4, 1536);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+               rtsx_clear_ms_error(chip);
+               TRACE_GOTO(chip, GetEKBFinish);
+       }
+       if (check_ms_err(chip)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+               rtsx_clear_ms_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       bufflen = min_t(int, 1052, scsi_bufflen(srb));
+       rtsx_stor_set_xfer_buf(buf, bufflen, srb);
+
+GetEKBFinish:
+       kfree(buf);
+       return retval;
+}
+
+int mg_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+       int bufflen;
+       int i;
+       unsigned int lun = SCSI_LUN(srb);
+       u8 buf[32];
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       ms_cleanup_work(chip);
+
+       retval = ms_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = mg_send_ex_cmd(chip, MG_GET_ID, 0);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = ms_read_bytes(chip, PRO_READ_SHORT_DATA, 32, WAIT_INT,
+                       buf, 32);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+       if (check_ms_err(chip)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+               rtsx_clear_ms_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       memcpy(ms_card->magic_gate_id, buf, 16);
+
+#ifdef READ_BYTES_WAIT_INT
+       retval = ms_poll_int(chip);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+#endif
+
+       retval = mg_send_ex_cmd(chip, MG_SET_RD, 0);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       bufflen = min_t(int, 12, scsi_bufflen(srb));
+       rtsx_stor_get_xfer_buf(buf, bufflen, srb);
+
+       for (i = 0; i < 8; i++)
+               buf[i] = buf[4+i];
+
+       for (i = 0; i < 24; i++)
+               buf[8+i] = 0;
+
+       retval = ms_write_bytes(chip, PRO_WRITE_SHORT_DATA,
+                               32, WAIT_INT, buf, 32);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+       if (check_ms_err(chip)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+               rtsx_clear_ms_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       ms_card->mg_auth = 0;
+
+       return STATUS_SUCCESS;
+}
+
+int mg_get_rsp_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+       int bufflen;
+       unsigned int lun = SCSI_LUN(srb);
+       u8 buf1[32], buf2[36];
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       ms_cleanup_work(chip);
+
+       retval = ms_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = mg_send_ex_cmd(chip, MG_MAKE_RMS, 0);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = ms_read_bytes(chip, PRO_READ_SHORT_DATA, 32, WAIT_INT,
+                       buf1, 32);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+       if (check_ms_err(chip)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+               rtsx_clear_ms_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       buf2[0] = 0x00;
+       buf2[1] = 0x22;
+       buf2[2] = 0x00;
+       buf2[3] = 0x00;
+
+       memcpy(buf2 + 4, ms_card->magic_gate_id, 16);
+       memcpy(buf2 + 20, buf1, 16);
+
+       bufflen = min_t(int, 36, scsi_bufflen(srb));
+       rtsx_stor_set_xfer_buf(buf2, bufflen, srb);
+
+#ifdef READ_BYTES_WAIT_INT
+       retval = ms_poll_int(chip);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+#endif
+
+       return STATUS_SUCCESS;
+}
+
+int mg_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+       int i;
+       int bufflen;
+       unsigned int lun = SCSI_LUN(srb);
+       u8 buf[32];
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       ms_cleanup_work(chip);
+
+       retval = ms_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = mg_send_ex_cmd(chip, MG_MAKE_KSE, 0);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       bufflen = min_t(int, 12, scsi_bufflen(srb));
+       rtsx_stor_get_xfer_buf(buf, bufflen, srb);
+
+       for (i = 0; i < 8; i++)
+               buf[i] = buf[4+i];
+
+       for (i = 0; i < 24; i++)
+               buf[8+i] = 0;
+
+       retval = ms_write_bytes(chip, PRO_WRITE_SHORT_DATA, 32, WAIT_INT,
+                               buf, 32);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+       if (check_ms_err(chip)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+               rtsx_clear_ms_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       ms_card->mg_auth = 1;
+
+       return STATUS_SUCCESS;
+}
+
+int mg_get_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+       int bufflen;
+       unsigned int lun = SCSI_LUN(srb);
+       u8 *buf = NULL;
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       ms_cleanup_work(chip);
+
+       retval = ms_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       buf = kmalloc(1028, GFP_KERNEL);
+       if (!buf)
+               TRACE_RET(chip, STATUS_ERROR);
+
+       buf[0] = 0x04;
+       buf[1] = 0x02;
+       buf[2] = 0x00;
+       buf[3] = 0x00;
+
+       retval = mg_send_ex_cmd(chip, MG_GET_IBD, ms_card->mg_entry_num);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+               TRACE_GOTO(chip, GetICVFinish);
+       }
+
+       retval = ms_transfer_data(chip, MS_TM_AUTO_READ, PRO_READ_LONG_DATA,
+                               2, WAIT_INT, 0, 0, buf + 4, 1024);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+               rtsx_clear_ms_error(chip);
+               TRACE_GOTO(chip, GetICVFinish);
+       }
+       if (check_ms_err(chip)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+               rtsx_clear_ms_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       bufflen = min_t(int, 1028, scsi_bufflen(srb));
+       rtsx_stor_set_xfer_buf(buf, bufflen, srb);
+
+GetICVFinish:
+       kfree(buf);
+       return retval;
+}
+
+int mg_set_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+       int bufflen;
+#ifdef MG_SET_ICV_SLOW
+       int i;
+#endif
+       unsigned int lun = SCSI_LUN(srb);
+       u8 *buf = NULL;
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       ms_cleanup_work(chip);
+
+       retval = ms_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       buf = kmalloc(1028, GFP_KERNEL);
+       if (!buf)
+               TRACE_RET(chip, STATUS_ERROR);
+
+       bufflen = min_t(int, 1028, scsi_bufflen(srb));
+       rtsx_stor_get_xfer_buf(buf, bufflen, srb);
+
+       retval = mg_send_ex_cmd(chip, MG_SET_IBD, ms_card->mg_entry_num);
+       if (retval != STATUS_SUCCESS) {
+               if (ms_card->mg_auth == 0) {
+                       if ((buf[5] & 0xC0) != 0)
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
+                       else
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MG_WRITE_ERR);
+               } else {
+                       set_sense_type(chip, lun, SENSE_TYPE_MG_WRITE_ERR);
+               }
+               TRACE_GOTO(chip, SetICVFinish);
+       }
+
+#ifdef MG_SET_ICV_SLOW
+       for (i = 0; i < 2; i++) {
+               udelay(50);
+
+               rtsx_init_cmd(chip);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC,
+                       0xFF, PRO_WRITE_LONG_DATA);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, WAIT_INT);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, RING_BUFFER);
+
+               trans_dma_enable(DMA_TO_DEVICE, chip, 512, DMA_512);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF,
+                               MS_TRANSFER_START |  MS_TM_NORMAL_WRITE);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER,
+                       MS_TRANSFER_END, MS_TRANSFER_END);
+
+               rtsx_send_cmd_no_wait(chip);
+
+               retval = rtsx_transfer_data(chip, MS_CARD, buf + 4 + i*512,
+                                       512, 0, DMA_TO_DEVICE, 3000);
+               if ((retval < 0) || check_ms_err(chip)) {
+                       rtsx_clear_ms_error(chip);
+                       if (ms_card->mg_auth == 0) {
+                               if ((buf[5] & 0xC0) != 0)
+                                       set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
+                               else
+                                       set_sense_type(chip, lun,
+                                               SENSE_TYPE_MG_WRITE_ERR);
+                       } else {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MG_WRITE_ERR);
+                       }
+                       retval = STATUS_FAIL;
+                       TRACE_GOTO(chip, SetICVFinish);
+               }
+       }
+#else
+       retval = ms_transfer_data(chip, MS_TM_AUTO_WRITE, PRO_WRITE_LONG_DATA,
+                               2, WAIT_INT, 0, 0, buf + 4, 1024);
+       if ((retval != STATUS_SUCCESS) || check_ms_err(chip)) {
+               rtsx_clear_ms_error(chip);
+               if (ms_card->mg_auth == 0) {
+                       if ((buf[5] & 0xC0) != 0)
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
+                       else
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MG_WRITE_ERR);
+               } else {
+                       set_sense_type(chip, lun, SENSE_TYPE_MG_WRITE_ERR);
+               }
+               TRACE_GOTO(chip, SetICVFinish);
+       }
+#endif
+
+SetICVFinish:
+       kfree(buf);
+       return retval;
+}
+
+#endif /* SUPPORT_MAGIC_GATE */
+
+void ms_cleanup_work(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+
+       if (CHK_MSPRO(ms_card)) {
+               if (ms_card->seq_mode) {
+                       RTSX_DEBUGP("MS Pro: stop transmission\n");
+                       mspro_stop_seq_mode(chip);
+                       ms_card->cleanup_counter = 0;
+               }
+               if (CHK_MSHG(ms_card)) {
+                       rtsx_write_register(chip, MS_CFG,
+                               MS_2K_SECTOR_MODE, 0x00);
+               }
+       }
+#ifdef MS_DELAY_WRITE
+       else if ((!CHK_MSPRO(ms_card)) && ms_card->delay_write.delay_write_flag) {
+               RTSX_DEBUGP("MS: delay write\n");
+               ms_delay_write(chip);
+               ms_card->cleanup_counter = 0;
+       }
+#endif
+}
+
+int ms_power_off_card3v3(struct rtsx_chip *chip)
+{
+       int retval;
+
+       retval = disable_card_clock(chip, MS_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (chip->asic_code) {
+               retval = ms_pull_ctl_disable(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       } else {
+               RTSX_WRITE_REG(chip, FPGA_PULL_CTL,
+                       FPGA_MS_PULL_CTL_BIT | 0x20, FPGA_MS_PULL_CTL_BIT);
+       }
+       RTSX_WRITE_REG(chip, CARD_OE, MS_OUTPUT_EN, 0);
+       if (!chip->ft2_fast_mode) {
+               retval = card_power_off(chip, MS_CARD);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int release_ms_card(struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+
+       RTSX_DEBUGP("release_ms_card\n");
+
+#ifdef MS_DELAY_WRITE
+       ms_card->delay_write.delay_write_flag = 0;
+#endif
+       ms_card->pro_under_formatting = 0;
+
+       chip->card_ready &= ~MS_CARD;
+       chip->card_fail &= ~MS_CARD;
+       chip->card_wp &= ~MS_CARD;
+
+       ms_free_l2p_tbl(chip);
+
+       memset(ms_card->raw_sys_info, 0, 96);
+#ifdef SUPPORT_PCGL_1P18
+       memset(ms_card->raw_model_name, 0, 48);
+#endif
+
+       retval = ms_power_off_card3v3(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
diff --git a/drivers/staging/rts5208/ms.h b/drivers/staging/rts5208/ms.h
new file mode 100644 (file)
index 0000000..26c5b03
--- /dev/null
@@ -0,0 +1,227 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_MS_H
+#define __REALTEK_RTSX_MS_H
+
+#define MS_DELAY_WRITE
+
+#define        MS_MAX_RETRY_COUNT      3
+
+#define        MS_EXTRA_SIZE           0x9
+
+#define        WRT_PRTCT               0x01
+
+/* Error Code */
+#define        MS_NO_ERROR                             0x00
+#define        MS_CRC16_ERROR                          0x80
+#define        MS_TO_ERROR                             0x40
+#define        MS_NO_CARD                              0x20
+#define        MS_NO_MEMORY                            0x10
+#define        MS_CMD_NK                               0x08
+#define        MS_FLASH_READ_ERROR                     0x04
+#define        MS_FLASH_WRITE_ERROR                    0x02
+#define        MS_BREQ_ERROR                           0x01
+#define        MS_NOT_FOUND                            0x03
+
+/* Transfer Protocol Command */
+#define READ_PAGE_DATA                         0x02
+#define READ_REG                               0x04
+#define        GET_INT                                 0x07
+#define WRITE_PAGE_DATA                                0x0D
+#define WRITE_REG                              0x0B
+#define SET_RW_REG_ADRS                                0x08
+#define SET_CMD                                        0x0E
+
+#define        PRO_READ_LONG_DATA                      0x02
+#define        PRO_READ_SHORT_DATA                     0x03
+#define PRO_READ_REG                           0x04
+#define        PRO_READ_QUAD_DATA                      0x05
+#define PRO_GET_INT                            0x07
+#define        PRO_WRITE_LONG_DATA                     0x0D
+#define        PRO_WRITE_SHORT_DATA                    0x0C
+#define        PRO_WRITE_QUAD_DATA                     0x0A
+#define PRO_WRITE_REG                          0x0B
+#define PRO_SET_RW_REG_ADRS                    0x08
+#define PRO_SET_CMD                            0x0E
+#define PRO_EX_SET_CMD                         0x09
+
+#ifdef SUPPORT_MAGIC_GATE
+
+#define MG_GET_ID              0x40
+#define MG_SET_LID             0x41
+#define MG_GET_LEKB            0x42
+#define MG_SET_RD              0x43
+#define MG_MAKE_RMS            0x44
+#define MG_MAKE_KSE            0x45
+#define MG_SET_IBD             0x46
+#define MG_GET_IBD             0x47
+
+#endif
+
+#ifdef XC_POWERCLASS
+#define XC_CHG_POWER           0x16
+#endif
+
+#define BLOCK_READ     0xAA
+#define        BLOCK_WRITE     0x55
+#define BLOCK_END      0x33
+#define BLOCK_ERASE    0x99
+#define FLASH_STOP     0xCC
+
+#define SLEEP          0x5A
+#define CLEAR_BUF      0xC3
+#define MS_RESET       0x3C
+
+#define PRO_READ_DATA          0x20
+#define        PRO_WRITE_DATA          0x21
+#define PRO_READ_ATRB          0x24
+#define PRO_STOP               0x25
+#define PRO_ERASE              0x26
+#define        PRO_READ_2K_DATA        0x27
+#define        PRO_WRITE_2K_DATA       0x28
+
+#define PRO_FORMAT             0x10
+#define PRO_SLEEP              0x11
+
+#define        IntReg                  0x01
+#define StatusReg0             0x02
+#define StatusReg1             0x03
+
+#define SystemParm             0x10
+#define BlockAdrs              0x11
+#define CMDParm                        0x14
+#define PageAdrs               0x15
+
+#define OverwriteFlag          0x16
+#define ManagemenFlag          0x17
+#define LogicalAdrs            0x18
+#define ReserveArea            0x1A
+
+#define        Pro_IntReg              0x01
+#define Pro_StatusReg          0x02
+#define Pro_TypeReg            0x04
+#define        Pro_IFModeReg           0x05
+#define Pro_CatagoryReg                0x06
+#define Pro_ClassReg           0x07
+
+
+#define Pro_SystemParm         0x10
+#define Pro_DataCount1         0x11
+#define Pro_DataCount0         0x12
+#define Pro_DataAddr3          0x13
+#define Pro_DataAddr2          0x14
+#define Pro_DataAddr1          0x15
+#define Pro_DataAddr0          0x16
+
+#define Pro_TPCParm            0x17
+#define Pro_CMDParm            0x18
+
+#define        INT_REG_CED             0x80
+#define        INT_REG_ERR             0x40
+#define        INT_REG_BREQ            0x20
+#define        INT_REG_CMDNK           0x01
+
+#define        BLOCK_BOOT              0xC0
+#define        BLOCK_OK                0x80
+#define        PAGE_OK                 0x60
+#define        DATA_COMPL              0x10
+
+#define        NOT_BOOT_BLOCK          0x4
+#define        NOT_TRANSLATION_TABLE   0x8
+
+#define        HEADER_ID0              PPBUF_BASE2
+#define        HEADER_ID1              (PPBUF_BASE2 + 1)
+#define        DISABLED_BLOCK0         (PPBUF_BASE2 + 0x170 + 4)
+#define        DISABLED_BLOCK1         (PPBUF_BASE2 + 0x170 + 5)
+#define        DISABLED_BLOCK2         (PPBUF_BASE2 + 0x170 + 6)
+#define        DISABLED_BLOCK3         (PPBUF_BASE2 + 0x170 + 7)
+#define        BLOCK_SIZE_0            (PPBUF_BASE2 + 0x1a0 + 2)
+#define        BLOCK_SIZE_1            (PPBUF_BASE2 + 0x1a0 + 3)
+#define        BLOCK_COUNT_0           (PPBUF_BASE2 + 0x1a0 + 4)
+#define        BLOCK_COUNT_1           (PPBUF_BASE2 + 0x1a0 + 5)
+#define        EBLOCK_COUNT_0          (PPBUF_BASE2 + 0x1a0 + 6)
+#define        EBLOCK_COUNT_1          (PPBUF_BASE2 + 0x1a0 + 7)
+#define        PAGE_SIZE_0             (PPBUF_BASE2 + 0x1a0 + 8)
+#define        PAGE_SIZE_1             (PPBUF_BASE2 + 0x1a0 + 9)
+
+#define MS_Device_Type         (PPBUF_BASE2 + 0x1D8)
+
+#define        MS_4bit_Support         (PPBUF_BASE2 + 0x1D3)
+
+#define setPS_NG       1
+#define setPS_Error    0
+
+#define        PARALLEL_8BIT_IF        0x40
+#define        PARALLEL_4BIT_IF        0x00
+#define        SERIAL_IF               0x80
+
+#define BUF_FULL       0x10
+#define BUF_EMPTY      0x20
+
+#define        MEDIA_BUSY      0x80
+#define        FLASH_BUSY      0x40
+#define        DATA_ERROR      0x20
+#define        STS_UCDT        0x10
+#define        EXTRA_ERROR     0x08
+#define        STS_UCEX        0x04
+#define        FLAG_ERROR      0x02
+#define        STS_UCFG        0x01
+
+#define MS_SHORT_DATA_LEN      32
+
+#define FORMAT_SUCCESS         0
+#define FORMAT_FAIL            1
+#define FORMAT_IN_PROGRESS     2
+
+#define        MS_SET_BAD_BLOCK_FLG(ms_card)   ((ms_card)->multi_flag |= 0x80)
+#define MS_CLR_BAD_BLOCK_FLG(ms_card)  ((ms_card)->multi_flag &= 0x7F)
+#define MS_TST_BAD_BLOCK_FLG(ms_card)  ((ms_card)->multi_flag & 0x80)
+
+void mspro_polling_format_status(struct rtsx_chip *chip);
+
+void mspro_stop_seq_mode(struct rtsx_chip *chip);
+int reset_ms_card(struct rtsx_chip *chip);
+int ms_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+       u32 start_sector, u16 sector_cnt);
+int mspro_format(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+               int short_data_len, int quick_format);
+void ms_free_l2p_tbl(struct rtsx_chip *chip);
+void ms_cleanup_work(struct rtsx_chip *chip);
+int ms_power_off_card3v3(struct rtsx_chip *chip);
+int release_ms_card(struct rtsx_chip *chip);
+#ifdef MS_DELAY_WRITE
+int ms_delay_write(struct rtsx_chip *chip);
+#endif
+
+#ifdef SUPPORT_MAGIC_GATE
+int mg_set_leaf_id(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int mg_get_local_EKB(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int mg_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int mg_get_rsp_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int mg_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int mg_get_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int mg_set_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+#endif
+
+#endif  /* __REALTEK_RTSX_MS_H */
diff --git a/drivers/staging/rts5208/rtsx.c b/drivers/staging/rts5208/rtsx.c
new file mode 100644 (file)
index 0000000..7882f57
--- /dev/null
@@ -0,0 +1,1069 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+
+#include "rtsx.h"
+#include "rtsx_chip.h"
+#include "rtsx_transport.h"
+#include "rtsx_scsi.h"
+#include "rtsx_card.h"
+#include "general.h"
+
+#include "ms.h"
+#include "sd.h"
+#include "xd.h"
+
+MODULE_DESCRIPTION("Realtek PCI-Express card reader rts5208/rts5288 driver");
+MODULE_LICENSE("GPL");
+
+static unsigned int delay_use = 1;
+module_param(delay_use, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
+
+static int ss_en;
+module_param(ss_en, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ss_en, "enable selective suspend");
+
+static int ss_interval = 50;
+module_param(ss_interval, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ss_interval, "Interval to enter ss state in seconds");
+
+static int auto_delink_en;
+module_param(auto_delink_en, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(auto_delink_en, "enable auto delink");
+
+static unsigned char aspm_l0s_l1_en;
+module_param(aspm_l0s_l1_en, byte, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(aspm_l0s_l1_en, "enable device aspm");
+
+static int msi_en;
+module_param(msi_en, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(msi_en, "enable msi");
+
+static irqreturn_t rtsx_interrupt(int irq, void *dev_id);
+
+/***********************************************************************
+ * Host functions
+ ***********************************************************************/
+
+static const char *host_info(struct Scsi_Host *host)
+{
+       return "SCSI emulation for PCI-Express Mass Storage devices";
+}
+
+static int slave_alloc(struct scsi_device *sdev)
+{
+       /*
+        * Set the INQUIRY transfer length to 36.  We don't use any of
+        * the extra data and many devices choke if asked for more or
+        * less than 36 bytes.
+        */
+       sdev->inquiry_len = 36;
+       return 0;
+}
+
+static int slave_configure(struct scsi_device *sdev)
+{
+       /* Scatter-gather buffers (all but the last) must have a length
+        * divisible by the bulk maxpacket size.  Otherwise a data packet
+        * would end up being short, causing a premature end to the data
+        * transfer.  Since high-speed bulk pipes have a maxpacket size
+        * of 512, we'll use that as the scsi device queue's DMA alignment
+        * mask.  Guaranteeing proper alignment of the first buffer will
+        * have the desired effect because, except at the beginning and
+        * the end, scatter-gather buffers follow page boundaries. */
+       blk_queue_dma_alignment(sdev->request_queue, (512 - 1));
+
+       /* Set the SCSI level to at least 2.  We'll leave it at 3 if that's
+        * what is originally reported.  We need this to avoid confusing
+        * the SCSI layer with devices that report 0 or 1, but need 10-byte
+        * commands (ala ATAPI devices behind certain bridges, or devices
+        * which simply have broken INQUIRY data).
+        *
+        * NOTE: This means /dev/sg programs (ala cdrecord) will get the
+        * actual information.  This seems to be the preference for
+        * programs like that.
+        *
+        * NOTE: This also means that /proc/scsi/scsi and sysfs may report
+        * the actual value or the modified one, depending on where the
+        * data comes from.
+        */
+       if (sdev->scsi_level < SCSI_2)
+               sdev->scsi_level = sdev->sdev_target->scsi_level = SCSI_2;
+
+       return 0;
+}
+
+
+/***********************************************************************
+ * /proc/scsi/ functions
+ ***********************************************************************/
+
+/* we use this macro to help us write into the buffer */
+#undef SPRINTF
+#define SPRINTF(args...) \
+       do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0)
+
+/* queue a command */
+/* This is always called with scsi_lock(host) held */
+static int queuecommand_lck(struct scsi_cmnd *srb,
+                       void (*done)(struct scsi_cmnd *))
+{
+       struct rtsx_dev *dev = host_to_rtsx(srb->device->host);
+       struct rtsx_chip *chip = dev->chip;
+
+       /* check for state-transition errors */
+       if (chip->srb != NULL) {
+               dev_err(&dev->pci->dev, "Error in %s: chip->srb = %p\n",
+                       __func__, chip->srb);
+               return SCSI_MLQUEUE_HOST_BUSY;
+       }
+
+       /* fail the command if we are disconnecting */
+       if (rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) {
+               dev_info(&dev->pci->dev, "Fail command during disconnect\n");
+               srb->result = DID_NO_CONNECT << 16;
+               done(srb);
+               return 0;
+       }
+
+       /* enqueue the command and wake up the control thread */
+       srb->scsi_done = done;
+       chip->srb = srb;
+       complete(&dev->cmnd_ready);
+
+       return 0;
+}
+
+static DEF_SCSI_QCMD(queuecommand)
+
+/***********************************************************************
+ * Error handling functions
+ ***********************************************************************/
+
+/* Command timeout and abort */
+static int command_abort(struct scsi_cmnd *srb)
+{
+       struct Scsi_Host *host = srb->device->host;
+       struct rtsx_dev *dev = host_to_rtsx(host);
+       struct rtsx_chip *chip = dev->chip;
+
+       dev_info(&dev->pci->dev, "%s called\n", __func__);
+
+       scsi_lock(host);
+
+       /* Is this command still active? */
+       if (chip->srb != srb) {
+               scsi_unlock(host);
+               dev_info(&dev->pci->dev, "-- nothing to abort\n");
+               return FAILED;
+       }
+
+       rtsx_set_stat(chip, RTSX_STAT_ABORT);
+
+       scsi_unlock(host);
+
+       /* Wait for the aborted command to finish */
+       wait_for_completion(&dev->notify);
+
+       return SUCCESS;
+}
+
+/* This invokes the transport reset mechanism to reset the state of the
+ * device */
+static int device_reset(struct scsi_cmnd *srb)
+{
+       int result = 0;
+       struct rtsx_dev *dev = host_to_rtsx(srb->device->host);
+
+       dev_info(&dev->pci->dev, "%s called\n", __func__);
+
+       return result < 0 ? FAILED : SUCCESS;
+}
+
+/* Simulate a SCSI bus reset by resetting the device's USB port. */
+static int bus_reset(struct scsi_cmnd *srb)
+{
+       int result = 0;
+       struct rtsx_dev *dev = host_to_rtsx(srb->device->host);
+
+       dev_info(&dev->pci->dev, "%s called\n", __func__);
+
+       return result < 0 ? FAILED : SUCCESS;
+}
+
+
+/*
+ * this defines our host template, with which we'll allocate hosts
+ */
+
+static struct scsi_host_template rtsx_host_template = {
+       /* basic userland interface stuff */
+       .name =                         CR_DRIVER_NAME,
+       .proc_name =                    CR_DRIVER_NAME,
+       .info =                         host_info,
+
+       /* command interface -- queued only */
+       .queuecommand =                 queuecommand,
+
+       /* error and abort handlers */
+       .eh_abort_handler =             command_abort,
+       .eh_device_reset_handler =      device_reset,
+       .eh_bus_reset_handler =         bus_reset,
+
+       /* queue commands only, only one command per LUN */
+       .can_queue =                    1,
+       .cmd_per_lun =                  1,
+
+       /* unknown initiator id */
+       .this_id =                      -1,
+
+       .slave_alloc =                  slave_alloc,
+       .slave_configure =              slave_configure,
+
+       /* lots of sg segments can be handled */
+       .sg_tablesize =                 SG_ALL,
+
+       /* limit the total size of a transfer to 120 KB */
+       .max_sectors =                  240,
+
+       /* merge commands... this seems to help performance, but
+        * periodically someone should test to see which setting is more
+        * optimal.
+        */
+       .use_clustering =               1,
+
+       /* emulated HBA */
+       .emulated =                     1,
+
+       /* we do our own delay after a device or bus reset */
+       .skip_settle_delay =            1,
+
+       /* module management */
+       .module =                       THIS_MODULE
+};
+
+
+static int rtsx_acquire_irq(struct rtsx_dev *dev)
+{
+       struct rtsx_chip *chip = dev->chip;
+
+       dev_info(&dev->pci->dev, "%s: chip->msi_en = %d, pci->irq = %d\n",
+                __func__, chip->msi_en, dev->pci->irq);
+
+       if (request_irq(dev->pci->irq, rtsx_interrupt,
+                       chip->msi_en ? 0 : IRQF_SHARED,
+                       CR_DRIVER_NAME, dev)) {
+               dev_err(&dev->pci->dev,
+                       "rtsx: unable to grab IRQ %d, disabling device\n",
+                       dev->pci->irq);
+               return -1;
+       }
+
+       dev->irq = dev->pci->irq;
+       pci_intx(dev->pci, !chip->msi_en);
+
+       return 0;
+}
+
+
+int rtsx_read_pci_cfg_byte(u8 bus, u8 dev, u8 func, u8 offset, u8 *val)
+{
+       struct pci_dev *pdev;
+       u8 data;
+       u8 devfn = (dev << 3) | func;
+
+       pdev = pci_get_bus_and_slot(bus, devfn);
+       if (!pdev)
+               return -1;
+
+       pci_read_config_byte(pdev, offset, &data);
+       if (val)
+               *val = data;
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int rtsx_suspend(struct pci_dev *pci, pm_message_t state)
+{
+       struct rtsx_dev *dev = (struct rtsx_dev *)pci_get_drvdata(pci);
+       struct rtsx_chip *chip;
+
+       if (!dev)
+               return 0;
+
+       /* lock the device pointers */
+       mutex_lock(&(dev->dev_mutex));
+
+       chip = dev->chip;
+
+       rtsx_do_before_power_down(chip, PM_S3);
+
+       if (dev->irq >= 0) {
+               synchronize_irq(dev->irq);
+               free_irq(dev->irq, (void *)dev);
+               dev->irq = -1;
+       }
+
+       if (chip->msi_en)
+               pci_disable_msi(pci);
+
+       pci_save_state(pci);
+       pci_enable_wake(pci, pci_choose_state(pci, state), 1);
+       pci_disable_device(pci);
+       pci_set_power_state(pci, pci_choose_state(pci, state));
+
+       /* unlock the device pointers */
+       mutex_unlock(&dev->dev_mutex);
+
+       return 0;
+}
+
+static int rtsx_resume(struct pci_dev *pci)
+{
+       struct rtsx_dev *dev = (struct rtsx_dev *)pci_get_drvdata(pci);
+       struct rtsx_chip *chip;
+
+       if (!dev)
+               return 0;
+
+       chip = dev->chip;
+
+       /* lock the device pointers */
+       mutex_lock(&(dev->dev_mutex));
+
+       pci_set_power_state(pci, PCI_D0);
+       pci_restore_state(pci);
+       if (pci_enable_device(pci) < 0) {
+               dev_err(&dev->pci->dev,
+                       "%s: pci_enable_device failed, disabling device\n",
+                       CR_DRIVER_NAME);
+               /* unlock the device pointers */
+               mutex_unlock(&dev->dev_mutex);
+               return -EIO;
+       }
+       pci_set_master(pci);
+
+       if (chip->msi_en) {
+               if (pci_enable_msi(pci) < 0)
+                       chip->msi_en = 0;
+       }
+
+       if (rtsx_acquire_irq(dev) < 0) {
+               /* unlock the device pointers */
+               mutex_unlock(&dev->dev_mutex);
+               return -EIO;
+       }
+
+       rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, 0x00);
+       rtsx_init_chip(chip);
+
+       /* unlock the device pointers */
+       mutex_unlock(&dev->dev_mutex);
+
+       return 0;
+}
+#endif /* CONFIG_PM */
+
+static void rtsx_shutdown(struct pci_dev *pci)
+{
+       struct rtsx_dev *dev = (struct rtsx_dev *)pci_get_drvdata(pci);
+       struct rtsx_chip *chip;
+
+       if (!dev)
+               return;
+
+       chip = dev->chip;
+
+       rtsx_do_before_power_down(chip, PM_S1);
+
+       if (dev->irq >= 0) {
+               synchronize_irq(dev->irq);
+               free_irq(dev->irq, (void *)dev);
+               dev->irq = -1;
+       }
+
+       if (chip->msi_en)
+               pci_disable_msi(pci);
+
+       pci_disable_device(pci);
+
+       return;
+}
+
+static int rtsx_control_thread(void *__dev)
+{
+       struct rtsx_dev *dev = (struct rtsx_dev *)__dev;
+       struct rtsx_chip *chip = dev->chip;
+       struct Scsi_Host *host = rtsx_to_host(dev);
+
+       for (;;) {
+               if (wait_for_completion_interruptible(&dev->cmnd_ready))
+                       break;
+
+               /* lock the device pointers */
+               mutex_lock(&(dev->dev_mutex));
+
+               /* if the device has disconnected, we are free to exit */
+               if (rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) {
+                       dev_info(&dev->pci->dev, "-- rtsx-control exiting\n");
+                       mutex_unlock(&dev->dev_mutex);
+                       break;
+               }
+
+               /* lock access to the state */
+               scsi_lock(host);
+
+               /* has the command aborted ? */
+               if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) {
+                       chip->srb->result = DID_ABORT << 16;
+                       goto SkipForAbort;
+               }
+
+               scsi_unlock(host);
+
+               /* reject the command if the direction indicator
+                * is UNKNOWN
+                */
+               if (chip->srb->sc_data_direction == DMA_BIDIRECTIONAL) {
+                       dev_err(&dev->pci->dev, "UNKNOWN data direction\n");
+                       chip->srb->result = DID_ERROR << 16;
+               }
+
+               /* reject if target != 0 or if LUN is higher than
+                * the maximum known LUN
+                */
+               else if (chip->srb->device->id) {
+                       dev_err(&dev->pci->dev, "Bad target number (%d:%d)\n",
+                               chip->srb->device->id,
+                               chip->srb->device->lun);
+                       chip->srb->result = DID_BAD_TARGET << 16;
+               }
+
+               else if (chip->srb->device->lun > chip->max_lun) {
+                       dev_err(&dev->pci->dev, "Bad LUN (%d:%d)\n",
+                               chip->srb->device->id,
+                               chip->srb->device->lun);
+                       chip->srb->result = DID_BAD_TARGET << 16;
+               }
+
+               /* we've got a command, let's do it! */
+               else {
+                       RTSX_DEBUG(scsi_show_command(chip->srb));
+                       rtsx_invoke_transport(chip->srb, chip);
+               }
+
+               /* lock access to the state */
+               scsi_lock(host);
+
+               /* did the command already complete because of a disconnect? */
+               if (!chip->srb)
+                       ;               /* nothing to do */
+
+               /* indicate that the command is done */
+               else if (chip->srb->result != DID_ABORT << 16) {
+                       chip->srb->scsi_done(chip->srb);
+               } else {
+SkipForAbort:
+                       dev_err(&dev->pci->dev, "scsi command aborted\n");
+               }
+
+               if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) {
+                       complete(&(dev->notify));
+
+                       rtsx_set_stat(chip, RTSX_STAT_IDLE);
+               }
+
+               /* finished working on this command */
+               chip->srb = NULL;
+               scsi_unlock(host);
+
+               /* unlock the device pointers */
+               mutex_unlock(&dev->dev_mutex);
+       } /* for (;;) */
+
+       /* notify the exit routine that we're actually exiting now
+        *
+        * complete()/wait_for_completion() is similar to up()/down(),
+        * except that complete() is safe in the case where the structure
+        * is getting deleted in a parallel mode of execution (i.e. just
+        * after the down() -- that's necessary for the thread-shutdown
+        * case.
+        *
+        * complete_and_exit() goes even further than this -- it is safe in
+        * the case that the thread of the caller is going away (not just
+        * the structure) -- this is necessary for the module-remove case.
+        * This is important in preemption kernels, which transfer the flow
+        * of execution immediately upon a complete().
+        */
+       complete_and_exit(&dev->control_exit, 0);
+}
+
+
+static int rtsx_polling_thread(void *__dev)
+{
+       struct rtsx_dev *dev = (struct rtsx_dev *)__dev;
+       struct rtsx_chip *chip = dev->chip;
+       struct sd_info *sd_card = &(chip->sd_card);
+       struct xd_info *xd_card = &(chip->xd_card);
+       struct ms_info *ms_card = &(chip->ms_card);
+
+       sd_card->cleanup_counter = 0;
+       xd_card->cleanup_counter = 0;
+       ms_card->cleanup_counter = 0;
+
+       /* Wait until SCSI scan finished */
+       wait_timeout((delay_use + 5) * 1000);
+
+       for (;;) {
+
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(POLLING_INTERVAL);
+
+               /* lock the device pointers */
+               mutex_lock(&(dev->dev_mutex));
+
+               /* if the device has disconnected, we are free to exit */
+               if (rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) {
+                       dev_info(&dev->pci->dev, "-- rtsx-polling exiting\n");
+                       mutex_unlock(&dev->dev_mutex);
+                       break;
+               }
+
+               mutex_unlock(&dev->dev_mutex);
+
+               mspro_polling_format_status(chip);
+
+               /* lock the device pointers */
+               mutex_lock(&(dev->dev_mutex));
+
+               rtsx_polling_func(chip);
+
+               /* unlock the device pointers */
+               mutex_unlock(&dev->dev_mutex);
+       }
+
+       complete_and_exit(&dev->polling_exit, 0);
+}
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t rtsx_interrupt(int irq, void *dev_id)
+{
+       struct rtsx_dev *dev = dev_id;
+       struct rtsx_chip *chip;
+       int retval;
+       u32 status;
+
+       if (dev)
+               chip = dev->chip;
+       else
+               return IRQ_NONE;
+
+       if (!chip)
+               return IRQ_NONE;
+
+       spin_lock(&dev->reg_lock);
+
+       retval = rtsx_pre_handle_interrupt(chip);
+       if (retval == STATUS_FAIL) {
+               spin_unlock(&dev->reg_lock);
+               if (chip->int_reg == 0xFFFFFFFF)
+                       return IRQ_HANDLED;
+               else
+                       return IRQ_NONE;
+       }
+
+       status = chip->int_reg;
+
+       if (dev->check_card_cd) {
+               if (!(dev->check_card_cd & status)) {
+                       /* card not exist, return TRANS_RESULT_FAIL */
+                       dev->trans_result = TRANS_RESULT_FAIL;
+                       if (dev->done)
+                               complete(dev->done);
+                       goto Exit;
+               }
+       }
+
+       if (status & (NEED_COMPLETE_INT | DELINK_INT)) {
+               if (status & (TRANS_FAIL_INT | DELINK_INT)) {
+                       if (status & DELINK_INT)
+                               RTSX_SET_DELINK(chip);
+                       dev->trans_result = TRANS_RESULT_FAIL;
+                       if (dev->done)
+                               complete(dev->done);
+               } else if (status & TRANS_OK_INT) {
+                       dev->trans_result = TRANS_RESULT_OK;
+                       if (dev->done)
+                               complete(dev->done);
+               } else if (status & DATA_DONE_INT) {
+                       dev->trans_result = TRANS_NOT_READY;
+                       if (dev->done && (dev->trans_state == STATE_TRANS_SG))
+                               complete(dev->done);
+               }
+       }
+
+Exit:
+       spin_unlock(&dev->reg_lock);
+       return IRQ_HANDLED;
+}
+
+
+/* Release all our dynamic resources */
+static void rtsx_release_resources(struct rtsx_dev *dev)
+{
+       dev_info(&dev->pci->dev, "-- %s\n", __func__);
+
+       /* Tell the control thread to exit.  The SCSI host must
+        * already have been removed so it won't try to queue
+        * any more commands.
+        */
+       dev_info(&dev->pci->dev, "-- sending exit command to thread\n");
+       complete(&dev->cmnd_ready);
+       if (dev->ctl_thread)
+               wait_for_completion(&dev->control_exit);
+       if (dev->polling_thread)
+               wait_for_completion(&dev->polling_exit);
+
+       wait_timeout(200);
+
+       if (dev->rtsx_resv_buf) {
+               dma_free_coherent(&(dev->pci->dev), RTSX_RESV_BUF_LEN,
+                               dev->rtsx_resv_buf, dev->rtsx_resv_buf_addr);
+               dev->chip->host_cmds_ptr = NULL;
+               dev->chip->host_sg_tbl_ptr = NULL;
+       }
+
+       if (dev->irq > 0)
+               free_irq(dev->irq, (void *)dev);
+       if (dev->chip->msi_en)
+               pci_disable_msi(dev->pci);
+       if (dev->remap_addr)
+               iounmap(dev->remap_addr);
+
+       pci_disable_device(dev->pci);
+       pci_release_regions(dev->pci);
+
+       rtsx_release_chip(dev->chip);
+       kfree(dev->chip);
+}
+
+/* First stage of disconnect processing: stop all commands and remove
+ * the host */
+static void quiesce_and_remove_host(struct rtsx_dev *dev)
+{
+       struct Scsi_Host *host = rtsx_to_host(dev);
+       struct rtsx_chip *chip = dev->chip;
+
+       /* Prevent new transfers, stop the current command, and
+        * interrupt a SCSI-scan or device-reset delay */
+       mutex_lock(&dev->dev_mutex);
+       scsi_lock(host);
+       rtsx_set_stat(chip, RTSX_STAT_DISCONNECT);
+       scsi_unlock(host);
+       mutex_unlock(&dev->dev_mutex);
+       wake_up(&dev->delay_wait);
+       wait_for_completion(&dev->scanning_done);
+
+       /* Wait some time to let other threads exist */
+       wait_timeout(100);
+
+       /* queuecommand won't accept any new commands and the control
+        * thread won't execute a previously-queued command.  If there
+        * is such a command pending, complete it with an error. */
+       mutex_lock(&dev->dev_mutex);
+       if (chip->srb) {
+               chip->srb->result = DID_NO_CONNECT << 16;
+               scsi_lock(host);
+               chip->srb->scsi_done(dev->chip->srb);
+               chip->srb = NULL;
+               scsi_unlock(host);
+       }
+       mutex_unlock(&dev->dev_mutex);
+
+       /* Now we own no commands so it's safe to remove the SCSI host */
+       scsi_remove_host(host);
+}
+
+/* Second stage of disconnect processing: deallocate all resources */
+static void release_everything(struct rtsx_dev *dev)
+{
+       rtsx_release_resources(dev);
+
+       /* Drop our reference to the host; the SCSI core will free it
+        * when the refcount becomes 0. */
+       scsi_host_put(rtsx_to_host(dev));
+}
+
+/* Thread to carry out delayed SCSI-device scanning */
+static int rtsx_scan_thread(void *__dev)
+{
+       struct rtsx_dev *dev = (struct rtsx_dev *)__dev;
+       struct rtsx_chip *chip = dev->chip;
+
+       /* Wait for the timeout to expire or for a disconnect */
+       if (delay_use > 0) {
+               dev_info(&dev->pci->dev,
+                        "%s: waiting for device to settle before scanning\n",
+                        CR_DRIVER_NAME);
+               wait_event_interruptible_timeout(dev->delay_wait,
+                               rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT),
+                               delay_use * HZ);
+       }
+
+       /* If the device is still connected, perform the scanning */
+       if (!rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) {
+               scsi_scan_host(rtsx_to_host(dev));
+               dev_info(&dev->pci->dev, "%s: device scan complete\n",
+                        CR_DRIVER_NAME);
+
+               /* Should we unbind if no devices were detected? */
+       }
+
+       complete_and_exit(&dev->scanning_done, 0);
+}
+
+static void rtsx_init_options(struct rtsx_chip *chip)
+{
+       chip->vendor_id = chip->rtsx->pci->vendor;
+       chip->product_id = chip->rtsx->pci->device;
+       chip->adma_mode = 1;
+       chip->lun_mc = 0;
+       chip->driver_first_load = 1;
+#ifdef HW_AUTO_SWITCH_SD_BUS
+       chip->sdio_in_charge = 0;
+#endif
+
+       chip->mspro_formatter_enable = 1;
+       chip->ignore_sd = 0;
+       chip->use_hw_setting = 0;
+       chip->lun_mode = DEFAULT_SINGLE;
+       chip->auto_delink_en = auto_delink_en;
+       chip->ss_en = ss_en;
+       chip->ss_idle_period = ss_interval * 1000;
+       chip->remote_wakeup_en = 0;
+       chip->aspm_l0s_l1_en = aspm_l0s_l1_en;
+       chip->dynamic_aspm = 1;
+       chip->fpga_sd_sdr104_clk = CLK_200;
+       chip->fpga_sd_ddr50_clk = CLK_100;
+       chip->fpga_sd_sdr50_clk = CLK_100;
+       chip->fpga_sd_hs_clk = CLK_100;
+       chip->fpga_mmc_52m_clk = CLK_80;
+       chip->fpga_ms_hg_clk = CLK_80;
+       chip->fpga_ms_4bit_clk = CLK_80;
+       chip->fpga_ms_1bit_clk = CLK_40;
+       chip->asic_sd_sdr104_clk = 203;
+       chip->asic_sd_sdr50_clk = 98;
+       chip->asic_sd_ddr50_clk = 98;
+       chip->asic_sd_hs_clk = 98;
+       chip->asic_mmc_52m_clk = 98;
+       chip->asic_ms_hg_clk = 117;
+       chip->asic_ms_4bit_clk = 78;
+       chip->asic_ms_1bit_clk = 39;
+       chip->ssc_depth_sd_sdr104 = SSC_DEPTH_2M;
+       chip->ssc_depth_sd_sdr50 = SSC_DEPTH_2M;
+       chip->ssc_depth_sd_ddr50 = SSC_DEPTH_1M;
+       chip->ssc_depth_sd_hs = SSC_DEPTH_1M;
+       chip->ssc_depth_mmc_52m = SSC_DEPTH_1M;
+       chip->ssc_depth_ms_hg = SSC_DEPTH_1M;
+       chip->ssc_depth_ms_4bit = SSC_DEPTH_512K;
+       chip->ssc_depth_low_speed = SSC_DEPTH_512K;
+       chip->ssc_en = 1;
+       chip->sd_speed_prior = 0x01040203;
+       chip->sd_current_prior = 0x00010203;
+       chip->sd_ctl = SD_PUSH_POINT_AUTO |
+                      SD_SAMPLE_POINT_AUTO |
+                      SUPPORT_MMC_DDR_MODE;
+       chip->sd_ddr_tx_phase = 0;
+       chip->mmc_ddr_tx_phase = 1;
+       chip->sd_default_tx_phase = 15;
+       chip->sd_default_rx_phase = 15;
+       chip->pmos_pwr_on_interval = 200;
+       chip->sd_voltage_switch_delay = 1000;
+       chip->ms_power_class_en = 3;
+
+       chip->sd_400mA_ocp_thd = 1;
+       chip->sd_800mA_ocp_thd = 5;
+       chip->ms_ocp_thd = 2;
+
+       chip->card_drive_sel = 0x55;
+       chip->sd30_drive_sel_1v8 = 0x03;
+       chip->sd30_drive_sel_3v3 = 0x01;
+
+       chip->do_delink_before_power_down = 1;
+       chip->auto_power_down = 1;
+       chip->polling_config = 0;
+
+       chip->force_clkreq_0 = 1;
+       chip->ft2_fast_mode = 0;
+
+       chip->sdio_retry_cnt = 1;
+
+       chip->xd_timeout = 2000;
+       chip->sd_timeout = 10000;
+       chip->ms_timeout = 2000;
+       chip->mspro_timeout = 15000;
+
+       chip->power_down_in_ss = 1;
+
+       chip->sdr104_en = 1;
+       chip->sdr50_en = 1;
+       chip->ddr50_en = 1;
+
+       chip->delink_stage1_step = 100;
+       chip->delink_stage2_step = 40;
+       chip->delink_stage3_step = 20;
+
+       chip->auto_delink_in_L1 = 1;
+       chip->blink_led = 1;
+       chip->msi_en = msi_en;
+       chip->hp_watch_bios_hotplug = 0;
+       chip->max_payload = 0;
+       chip->phy_voltage = 0;
+
+       chip->support_ms_8bit = 1;
+       chip->s3_pwr_off_delay = 1000;
+}
+
+static int rtsx_probe(struct pci_dev *pci,
+                               const struct pci_device_id *pci_id)
+{
+       struct Scsi_Host *host;
+       struct rtsx_dev *dev;
+       int err = 0;
+       struct task_struct *th;
+
+       RTSX_DEBUGP("Realtek PCI-E card reader detected\n");
+
+       err = pci_enable_device(pci);
+       if (err < 0) {
+               dev_err(&pci->dev, "PCI enable device failed!\n");
+               return err;
+       }
+
+       err = pci_request_regions(pci, CR_DRIVER_NAME);
+       if (err < 0) {
+               dev_err(&pci->dev, "PCI request regions for %s failed!\n",
+                       CR_DRIVER_NAME);
+               pci_disable_device(pci);
+               return err;
+       }
+
+       /*
+        * Ask the SCSI layer to allocate a host structure, with extra
+        * space at the end for our private rtsx_dev structure.
+        */
+       host = scsi_host_alloc(&rtsx_host_template, sizeof(*dev));
+       if (!host) {
+               dev_err(&pci->dev, "Unable to allocate the scsi host\n");
+               pci_release_regions(pci);
+               pci_disable_device(pci);
+               return -ENOMEM;
+       }
+
+       dev = host_to_rtsx(host);
+       memset(dev, 0, sizeof(struct rtsx_dev));
+
+       dev->chip = kzalloc(sizeof(struct rtsx_chip), GFP_KERNEL);
+       if (dev->chip == NULL)
+               goto errout;
+
+       spin_lock_init(&dev->reg_lock);
+       mutex_init(&(dev->dev_mutex));
+       init_completion(&dev->cmnd_ready);
+       init_completion(&dev->control_exit);
+       init_completion(&dev->polling_exit);
+       init_completion(&(dev->notify));
+       init_completion(&dev->scanning_done);
+       init_waitqueue_head(&dev->delay_wait);
+
+       dev->pci = pci;
+       dev->irq = -1;
+
+       dev_info(&pci->dev, "Resource length: 0x%x\n",
+                (unsigned int)pci_resource_len(pci, 0));
+       dev->addr = pci_resource_start(pci, 0);
+       dev->remap_addr = ioremap_nocache(dev->addr, pci_resource_len(pci, 0));
+       if (dev->remap_addr == NULL) {
+               dev_err(&pci->dev, "ioremap error\n");
+               err = -ENXIO;
+               goto errout;
+       }
+
+       /*
+        * Using "unsigned long" cast here to eliminate gcc warning in
+        * 64-bit system
+        */
+       dev_info(&pci->dev, "Original address: 0x%lx, remapped address: 0x%lx\n",
+                (unsigned long)(dev->addr), (unsigned long)(dev->remap_addr));
+
+       dev->rtsx_resv_buf = dma_alloc_coherent(&(pci->dev), RTSX_RESV_BUF_LEN,
+                       &(dev->rtsx_resv_buf_addr), GFP_KERNEL);
+       if (dev->rtsx_resv_buf == NULL) {
+               dev_err(&pci->dev, "alloc dma buffer fail\n");
+               err = -ENXIO;
+               goto errout;
+       }
+       dev->chip->host_cmds_ptr = dev->rtsx_resv_buf;
+       dev->chip->host_cmds_addr = dev->rtsx_resv_buf_addr;
+       dev->chip->host_sg_tbl_ptr = dev->rtsx_resv_buf + HOST_CMDS_BUF_LEN;
+       dev->chip->host_sg_tbl_addr = dev->rtsx_resv_buf_addr +
+                                     HOST_CMDS_BUF_LEN;
+
+       dev->chip->rtsx = dev;
+
+       rtsx_init_options(dev->chip);
+
+       dev_info(&pci->dev, "pci->irq = %d\n", pci->irq);
+
+       if (dev->chip->msi_en) {
+               if (pci_enable_msi(pci) < 0)
+                       dev->chip->msi_en = 0;
+       }
+
+       if (rtsx_acquire_irq(dev) < 0) {
+               err = -EBUSY;
+               goto errout;
+       }
+
+       pci_set_master(pci);
+       synchronize_irq(dev->irq);
+
+       rtsx_init_chip(dev->chip);
+
+       /* set the supported max_lun and max_id for the scsi host
+        * NOTE: the minimal value of max_id is 1 */
+       host->max_id = 1;
+       host->max_lun = dev->chip->max_lun;
+
+       /* Start up our control thread */
+       th = kthread_run(rtsx_control_thread, dev, CR_DRIVER_NAME);
+       if (IS_ERR(th)) {
+               dev_err(&pci->dev, "Unable to start control thread\n");
+               err = PTR_ERR(th);
+               goto errout;
+       }
+       dev->ctl_thread = th;
+
+       err = scsi_add_host(host, &pci->dev);
+       if (err) {
+               dev_err(&pci->dev, "Unable to add the scsi host\n");
+               goto errout;
+       }
+
+       /* Start up the thread for delayed SCSI-device scanning */
+       th = kthread_run(rtsx_scan_thread, dev, "rtsx-scan");
+       if (IS_ERR(th)) {
+               dev_err(&pci->dev, "Unable to start the device-scanning thread\n");
+               complete(&dev->scanning_done);
+               quiesce_and_remove_host(dev);
+               err = PTR_ERR(th);
+               goto errout;
+       }
+
+       /* Start up the thread for polling thread */
+       th = kthread_run(rtsx_polling_thread, dev, "rtsx-polling");
+       if (IS_ERR(th)) {
+               dev_err(&pci->dev, "Unable to start the device-polling thread\n");
+               quiesce_and_remove_host(dev);
+               err = PTR_ERR(th);
+               goto errout;
+       }
+       dev->polling_thread = th;
+
+       pci_set_drvdata(pci, dev);
+
+       return 0;
+
+       /* We come here if there are any problems */
+errout:
+       dev_err(&pci->dev, "rtsx_probe() failed\n");
+       release_everything(dev);
+
+       return err;
+}
+
+
+static void rtsx_remove(struct pci_dev *pci)
+{
+       struct rtsx_dev *dev = (struct rtsx_dev *)pci_get_drvdata(pci);
+
+       dev_info(&pci->dev, "rtsx_remove() called\n");
+
+       quiesce_and_remove_host(dev);
+       release_everything(dev);
+
+       pci_set_drvdata(pci, NULL);
+}
+
+/* PCI IDs */
+static DEFINE_PCI_DEVICE_TABLE(rtsx_ids) = {
+       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x5208), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+       { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x5288), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+       { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, rtsx_ids);
+
+/* pci_driver definition */
+static struct pci_driver driver = {
+       .name = CR_DRIVER_NAME,
+       .id_table = rtsx_ids,
+       .probe = rtsx_probe,
+       .remove = rtsx_remove,
+#ifdef CONFIG_PM
+       .suspend = rtsx_suspend,
+       .resume = rtsx_resume,
+#endif
+       .shutdown = rtsx_shutdown,
+};
+
+static int __init rtsx_init(void)
+{
+       pr_info("Initializing Realtek PCIE storage driver...\n");
+
+       return pci_register_driver(&driver);
+}
+
+static void __exit rtsx_exit(void)
+{
+       pr_info("rtsx_exit() called\n");
+
+       pci_unregister_driver(&driver);
+
+       pr_info("%s module exit\n", CR_DRIVER_NAME);
+}
+
+module_init(rtsx_init)
+module_exit(rtsx_exit)
diff --git a/drivers/staging/rts5208/rtsx.h b/drivers/staging/rts5208/rtsx.h
new file mode 100644 (file)
index 0000000..3f048e0
--- /dev/null
@@ -0,0 +1,186 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_H
+#define __REALTEK_RTSX_H
+
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <linux/cdrom.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/time.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_devinfo.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+
+#include "debug.h"
+#include "trace.h"
+#include "general.h"
+
+#define CR_DRIVER_NAME         "rts5208"
+
+#define pci_get_bus_and_slot(bus, devfn)       \
+       pci_get_domain_bus_and_slot(0, (bus), (devfn))
+
+/*
+ * macros for easy use
+ */
+#define rtsx_writel(chip, reg, value) \
+       iowrite32(value, (chip)->rtsx->remap_addr + reg)
+#define rtsx_readl(chip, reg) \
+       ioread32((chip)->rtsx->remap_addr + reg)
+#define rtsx_writew(chip, reg, value) \
+       iowrite16(value, (chip)->rtsx->remap_addr + reg)
+#define rtsx_readw(chip, reg) \
+       ioread16((chip)->rtsx->remap_addr + reg)
+#define rtsx_writeb(chip, reg, value) \
+       iowrite8(value, (chip)->rtsx->remap_addr + reg)
+#define rtsx_readb(chip, reg) \
+       ioread8((chip)->rtsx->remap_addr + reg)
+
+#define rtsx_read_config_byte(chip, where, val) \
+       pci_read_config_byte((chip)->rtsx->pci, where, val)
+
+#define rtsx_write_config_byte(chip, where, val) \
+       pci_write_config_byte((chip)->rtsx->pci, where, val)
+
+#define wait_timeout_x(task_state, msecs)              \
+do {                                                   \
+               set_current_state((task_state));        \
+               schedule_timeout((msecs) * HZ / 1000);  \
+} while (0)
+#define wait_timeout(msecs)    wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))
+
+
+#define STATE_TRANS_NONE       0
+#define STATE_TRANS_CMD                1
+#define STATE_TRANS_BUF                2
+#define STATE_TRANS_SG         3
+
+#define TRANS_NOT_READY                0
+#define TRANS_RESULT_OK                1
+#define TRANS_RESULT_FAIL      2
+
+#define SCSI_LUN(srb)          ((srb)->device->lun)
+
+typedef unsigned long DELAY_PARA_T;
+
+struct rtsx_chip;
+
+struct rtsx_dev {
+       struct pci_dev *pci;
+
+       /* pci resources */
+       unsigned long           addr;
+       void __iomem            *remap_addr;
+       int irq;
+
+       /* locks */
+       spinlock_t              reg_lock;
+
+       struct task_struct      *ctl_thread;     /* the control thread   */
+       struct task_struct      *polling_thread; /* the polling thread   */
+
+       /* mutual exclusion and synchronization structures */
+       struct completion       cmnd_ready;      /* to sleep thread on      */
+       struct completion       control_exit;    /* control thread exit     */
+       struct completion       polling_exit;    /* polling thread exit     */
+       struct completion       notify;          /* thread begin/end        */
+       struct completion       scanning_done;   /* wait for scan thread    */
+
+       wait_queue_head_t       delay_wait;      /* wait during scan, reset */
+       struct mutex            dev_mutex;
+
+       /* host reserved buffer */
+       void                    *rtsx_resv_buf;
+       dma_addr_t              rtsx_resv_buf_addr;
+
+       char                    trans_result;
+       char                    trans_state;
+
+       struct completion       *done;
+       /* Whether interrupt handler should care card cd info */
+       u32                     check_card_cd;
+
+       struct rtsx_chip        *chip;
+};
+
+typedef struct rtsx_dev rtsx_dev_t;
+
+/* Convert between rtsx_dev and the corresponding Scsi_Host */
+static inline struct Scsi_Host *rtsx_to_host(struct rtsx_dev *dev)
+{
+       return container_of((void *) dev, struct Scsi_Host, hostdata);
+}
+static inline struct rtsx_dev *host_to_rtsx(struct Scsi_Host *host)
+{
+       return (struct rtsx_dev *) host->hostdata;
+}
+
+static inline void get_current_time(u8 *timeval_buf, int buf_len)
+{
+       struct timeval tv;
+
+       if (!timeval_buf || (buf_len < 8))
+               return;
+
+       do_gettimeofday(&tv);
+
+       timeval_buf[0] = (u8)(tv.tv_sec >> 24);
+       timeval_buf[1] = (u8)(tv.tv_sec >> 16);
+       timeval_buf[2] = (u8)(tv.tv_sec >> 8);
+       timeval_buf[3] = (u8)(tv.tv_sec);
+       timeval_buf[4] = (u8)(tv.tv_usec >> 24);
+       timeval_buf[5] = (u8)(tv.tv_usec >> 16);
+       timeval_buf[6] = (u8)(tv.tv_usec >> 8);
+       timeval_buf[7] = (u8)(tv.tv_usec);
+}
+
+/* The scsi_lock() and scsi_unlock() macros protect the sm_state and the
+ * single queue element srb for write access */
+#define scsi_unlock(host)      spin_unlock_irq(host->host_lock)
+#define scsi_lock(host)                spin_lock_irq(host->host_lock)
+
+#define lock_state(chip)       spin_lock_irq(&((chip)->rtsx->reg_lock))
+#define unlock_state(chip)     spin_unlock_irq(&((chip)->rtsx->reg_lock))
+
+/* struct scsi_cmnd transfer buffer access utilities */
+enum xfer_buf_dir      {TO_XFER_BUF, FROM_XFER_BUF};
+
+int rtsx_read_pci_cfg_byte(u8 bus, u8 dev, u8 func, u8 offset, u8 *val);
+
+#endif  /* __REALTEK_RTSX_H */
diff --git a/drivers/staging/rts5208/rtsx_card.c b/drivers/staging/rts5208/rtsx_card.c
new file mode 100644 (file)
index 0000000..3055eb1
--- /dev/null
@@ -0,0 +1,1126 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/kernel.h>
+
+#include "rtsx.h"
+#include "rtsx_transport.h"
+#include "rtsx_scsi.h"
+#include "rtsx_card.h"
+
+#include "rtsx_sys.h"
+#include "general.h"
+
+#include "sd.h"
+#include "xd.h"
+#include "ms.h"
+
+void do_remaining_work(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+#ifdef XD_DELAY_WRITE
+       struct xd_info *xd_card = &(chip->xd_card);
+#endif
+       struct ms_info *ms_card = &(chip->ms_card);
+
+       if (chip->card_ready & SD_CARD) {
+               if (sd_card->seq_mode) {
+                       rtsx_set_stat(chip, RTSX_STAT_RUN);
+                       sd_card->cleanup_counter++;
+               } else {
+                       sd_card->cleanup_counter = 0;
+               }
+       }
+
+#ifdef XD_DELAY_WRITE
+       if (chip->card_ready & XD_CARD) {
+               if (xd_card->delay_write.delay_write_flag) {
+                       rtsx_set_stat(chip, RTSX_STAT_RUN);
+                       xd_card->cleanup_counter++;
+               } else {
+                       xd_card->cleanup_counter = 0;
+               }
+       }
+#endif
+
+       if (chip->card_ready & MS_CARD) {
+               if (CHK_MSPRO(ms_card)) {
+                       if (ms_card->seq_mode) {
+                               rtsx_set_stat(chip, RTSX_STAT_RUN);
+                               ms_card->cleanup_counter++;
+                       } else {
+                               ms_card->cleanup_counter = 0;
+                       }
+               } else {
+#ifdef MS_DELAY_WRITE
+                       if (ms_card->delay_write.delay_write_flag) {
+                               rtsx_set_stat(chip, RTSX_STAT_RUN);
+                               ms_card->cleanup_counter++;
+                       } else {
+                               ms_card->cleanup_counter = 0;
+                       }
+#endif
+               }
+       }
+
+       if (sd_card->cleanup_counter > POLLING_WAIT_CNT)
+               sd_cleanup_work(chip);
+
+       if (xd_card->cleanup_counter > POLLING_WAIT_CNT)
+               xd_cleanup_work(chip);
+
+       if (ms_card->cleanup_counter > POLLING_WAIT_CNT)
+               ms_cleanup_work(chip);
+}
+
+void try_to_switch_sdio_ctrl(struct rtsx_chip *chip)
+{
+       u8 reg1 = 0, reg2 = 0;
+
+       rtsx_read_register(chip, 0xFF34, &reg1);
+       rtsx_read_register(chip, 0xFF38, &reg2);
+       RTSX_DEBUGP("reg 0xFF34: 0x%x, reg 0xFF38: 0x%x\n", reg1, reg2);
+       if ((reg1 & 0xC0) && (reg2 & 0xC0)) {
+               chip->sd_int = 1;
+               rtsx_write_register(chip, SDIO_CTRL, 0xFF, SDIO_BUS_CTRL | SDIO_CD_CTRL);
+               rtsx_write_register(chip, PWR_GATE_CTRL, LDO3318_PWR_MASK, LDO_ON);
+       }
+}
+
+#ifdef SUPPORT_SDIO_ASPM
+void dynamic_configure_sdio_aspm(struct rtsx_chip *chip)
+{
+       u8 buf[12], reg;
+       int i;
+
+       for (i = 0; i < 12; i++)
+               rtsx_read_register(chip, 0xFF08 + i, &buf[i]);
+       rtsx_read_register(chip, 0xFF25, &reg);
+       if ((memcmp(buf, chip->sdio_raw_data, 12) != 0) || (reg & 0x03)) {
+               chip->sdio_counter = 0;
+               chip->sdio_idle = 0;
+       } else {
+               if (!chip->sdio_idle) {
+                       chip->sdio_counter++;
+                       if (chip->sdio_counter >= SDIO_IDLE_COUNT) {
+                               chip->sdio_counter = 0;
+                               chip->sdio_idle = 1;
+                       }
+               }
+       }
+       memcpy(chip->sdio_raw_data, buf, 12);
+
+       if (chip->sdio_idle) {
+               if (!chip->sdio_aspm) {
+                       RTSX_DEBUGP("SDIO enter ASPM!\n");
+                       rtsx_write_register(chip, ASPM_FORCE_CTL, 0xFC,
+                                       0x30 | (chip->aspm_level[1] << 2));
+                       chip->sdio_aspm = 1;
+               }
+       } else {
+               if (chip->sdio_aspm) {
+                       RTSX_DEBUGP("SDIO exit ASPM!\n");
+                       rtsx_write_register(chip, ASPM_FORCE_CTL, 0xFC, 0x30);
+                       chip->sdio_aspm = 0;
+               }
+       }
+}
+#endif
+
+void do_reset_sd_card(struct rtsx_chip *chip)
+{
+       int retval;
+
+       RTSX_DEBUGP("%s: %d, card2lun = 0x%x\n", __func__,
+                    chip->sd_reset_counter, chip->card2lun[SD_CARD]);
+
+       if (chip->card2lun[SD_CARD] >= MAX_ALLOWED_LUN_CNT) {
+               clear_bit(SD_NR, &(chip->need_reset));
+               chip->sd_reset_counter = 0;
+               chip->sd_show_cnt = 0;
+               return;
+       }
+
+       chip->rw_fail_cnt[chip->card2lun[SD_CARD]] = 0;
+
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+       rtsx_write_register(chip, SDIO_CTRL, 0xFF, 0);
+
+       retval = reset_sd_card(chip);
+       if (chip->need_release & SD_CARD)
+               return;
+       if (retval == STATUS_SUCCESS) {
+               clear_bit(SD_NR, &(chip->need_reset));
+               chip->sd_reset_counter = 0;
+               chip->sd_show_cnt = 0;
+               chip->card_ready |= SD_CARD;
+               chip->card_fail &= ~SD_CARD;
+               chip->rw_card[chip->card2lun[SD_CARD]] = sd_rw;
+       } else {
+               if (chip->sd_io || (chip->sd_reset_counter >= MAX_RESET_CNT)) {
+                       clear_bit(SD_NR, &(chip->need_reset));
+                       chip->sd_reset_counter = 0;
+                       chip->sd_show_cnt = 0;
+               } else {
+                       chip->sd_reset_counter++;
+               }
+               chip->card_ready &= ~SD_CARD;
+               chip->card_fail |= SD_CARD;
+               chip->capacity[chip->card2lun[SD_CARD]] = 0;
+               chip->rw_card[chip->card2lun[SD_CARD]] = NULL;
+
+               rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN, 0);
+               if (!chip->ft2_fast_mode)
+                       card_power_off(chip, SD_CARD);
+               if (chip->sd_io) {
+                       chip->sd_int = 0;
+                       try_to_switch_sdio_ctrl(chip);
+               } else {
+                       disable_card_clock(chip, SD_CARD);
+               }
+       }
+}
+
+void do_reset_xd_card(struct rtsx_chip *chip)
+{
+       int retval;
+
+       RTSX_DEBUGP("%s: %d, card2lun = 0x%x\n", __func__,
+                    chip->xd_reset_counter, chip->card2lun[XD_CARD]);
+
+       if (chip->card2lun[XD_CARD] >= MAX_ALLOWED_LUN_CNT) {
+               clear_bit(XD_NR, &(chip->need_reset));
+               chip->xd_reset_counter = 0;
+               chip->xd_show_cnt = 0;
+               return;
+       }
+
+       chip->rw_fail_cnt[chip->card2lun[XD_CARD]] = 0;
+
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+       rtsx_write_register(chip, SDIO_CTRL, 0xFF, 0);
+
+       retval = reset_xd_card(chip);
+       if (chip->need_release & XD_CARD)
+               return;
+       if (retval == STATUS_SUCCESS) {
+               clear_bit(XD_NR, &(chip->need_reset));
+               chip->xd_reset_counter = 0;
+               chip->card_ready |= XD_CARD;
+               chip->card_fail &= ~XD_CARD;
+               chip->rw_card[chip->card2lun[XD_CARD]] = xd_rw;
+       } else {
+               if (chip->xd_reset_counter >= MAX_RESET_CNT) {
+                       clear_bit(XD_NR, &(chip->need_reset));
+                       chip->xd_reset_counter = 0;
+                       chip->xd_show_cnt = 0;
+               } else {
+                       chip->xd_reset_counter++;
+               }
+               chip->card_ready &= ~XD_CARD;
+               chip->card_fail |= XD_CARD;
+               chip->capacity[chip->card2lun[XD_CARD]] = 0;
+               chip->rw_card[chip->card2lun[XD_CARD]] = NULL;
+
+               rtsx_write_register(chip, CARD_OE, XD_OUTPUT_EN, 0);
+               if (!chip->ft2_fast_mode)
+                       card_power_off(chip, XD_CARD);
+               disable_card_clock(chip, XD_CARD);
+       }
+}
+
+void do_reset_ms_card(struct rtsx_chip *chip)
+{
+       int retval;
+
+       RTSX_DEBUGP("%s: %d, card2lun = 0x%x\n", __func__,
+                    chip->ms_reset_counter, chip->card2lun[MS_CARD]);
+
+       if (chip->card2lun[MS_CARD] >= MAX_ALLOWED_LUN_CNT) {
+               clear_bit(MS_NR, &(chip->need_reset));
+               chip->ms_reset_counter = 0;
+               chip->ms_show_cnt = 0;
+               return;
+       }
+
+       chip->rw_fail_cnt[chip->card2lun[MS_CARD]] = 0;
+
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+       rtsx_write_register(chip, SDIO_CTRL, 0xFF, 0);
+
+       retval = reset_ms_card(chip);
+       if (chip->need_release & MS_CARD)
+               return;
+       if (retval == STATUS_SUCCESS) {
+               clear_bit(MS_NR, &(chip->need_reset));
+               chip->ms_reset_counter = 0;
+               chip->card_ready |= MS_CARD;
+               chip->card_fail &= ~MS_CARD;
+               chip->rw_card[chip->card2lun[MS_CARD]] = ms_rw;
+       } else {
+               if (chip->ms_reset_counter >= MAX_RESET_CNT) {
+                       clear_bit(MS_NR, &(chip->need_reset));
+                       chip->ms_reset_counter = 0;
+                       chip->ms_show_cnt = 0;
+               } else {
+                       chip->ms_reset_counter++;
+               }
+               chip->card_ready &= ~MS_CARD;
+               chip->card_fail |= MS_CARD;
+               chip->capacity[chip->card2lun[MS_CARD]] = 0;
+               chip->rw_card[chip->card2lun[MS_CARD]] = NULL;
+
+               rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN, 0);
+               if (!chip->ft2_fast_mode)
+                       card_power_off(chip, MS_CARD);
+               disable_card_clock(chip, MS_CARD);
+       }
+}
+
+static void release_sdio(struct rtsx_chip *chip)
+{
+       if (chip->sd_io) {
+               rtsx_write_register(chip, CARD_STOP, SD_STOP | SD_CLR_ERR,
+                               SD_STOP | SD_CLR_ERR);
+
+               if (chip->chip_insert_with_sdio) {
+                       chip->chip_insert_with_sdio = 0;
+
+                       if (CHECK_PID(chip, 0x5288))
+                               rtsx_write_register(chip, 0xFE5A, 0x08, 0x00);
+                       else
+                               rtsx_write_register(chip, 0xFE70, 0x80, 0x00);
+               }
+
+               rtsx_write_register(chip, SDIO_CTRL, SDIO_CD_CTRL, 0);
+               chip->sd_io = 0;
+       }
+}
+
+void rtsx_power_off_card(struct rtsx_chip *chip)
+{
+       if ((chip->card_ready & SD_CARD) || chip->sd_io) {
+               sd_cleanup_work(chip);
+               sd_power_off_card3v3(chip);
+       }
+
+       if (chip->card_ready & XD_CARD) {
+               xd_cleanup_work(chip);
+               xd_power_off_card3v3(chip);
+       }
+
+       if (chip->card_ready & MS_CARD) {
+               ms_cleanup_work(chip);
+               ms_power_off_card3v3(chip);
+       }
+}
+
+void rtsx_release_cards(struct rtsx_chip *chip)
+{
+       chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
+
+       if ((chip->card_ready & SD_CARD) || chip->sd_io) {
+               if (chip->int_reg & SD_EXIST)
+                       sd_cleanup_work(chip);
+               release_sd_card(chip);
+       }
+
+       if (chip->card_ready & XD_CARD) {
+               if (chip->int_reg & XD_EXIST)
+                       xd_cleanup_work(chip);
+               release_xd_card(chip);
+       }
+
+       if (chip->card_ready & MS_CARD) {
+               if (chip->int_reg & MS_EXIST)
+                       ms_cleanup_work(chip);
+               release_ms_card(chip);
+       }
+}
+
+void rtsx_reset_cards(struct rtsx_chip *chip)
+{
+       if (!chip->need_reset)
+               return;
+
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       rtsx_force_power_on(chip, SSC_PDCTL | OC_PDCTL);
+
+       rtsx_disable_aspm(chip);
+
+       if ((chip->need_reset & SD_CARD) && chip->chip_insert_with_sdio)
+               clear_bit(SD_NR, &(chip->need_reset));
+
+       if (chip->need_reset & XD_CARD) {
+               chip->card_exist |= XD_CARD;
+
+               if (chip->xd_show_cnt >= MAX_SHOW_CNT)
+                       do_reset_xd_card(chip);
+               else
+                       chip->xd_show_cnt++;
+       }
+       if (CHECK_PID(chip, 0x5288) && CHECK_BARO_PKG(chip, QFN)) {
+               if (chip->card_exist & XD_CARD) {
+                       clear_bit(SD_NR, &(chip->need_reset));
+                       clear_bit(MS_NR, &(chip->need_reset));
+               }
+       }
+       if (chip->need_reset & SD_CARD) {
+               chip->card_exist |= SD_CARD;
+
+               if (chip->sd_show_cnt >= MAX_SHOW_CNT) {
+                       rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
+                       do_reset_sd_card(chip);
+               } else {
+                       chip->sd_show_cnt++;
+               }
+       }
+       if (chip->need_reset & MS_CARD) {
+               chip->card_exist |= MS_CARD;
+
+               if (chip->ms_show_cnt >= MAX_SHOW_CNT)
+                       do_reset_ms_card(chip);
+               else
+                       chip->ms_show_cnt++;
+       }
+}
+
+void rtsx_reinit_cards(struct rtsx_chip *chip, int reset_chip)
+{
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       rtsx_force_power_on(chip, SSC_PDCTL | OC_PDCTL);
+
+       if (reset_chip)
+               rtsx_reset_chip(chip);
+
+       chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
+
+       if ((chip->int_reg & SD_EXIST) && (chip->need_reinit & SD_CARD)) {
+               release_sdio(chip);
+               release_sd_card(chip);
+
+               wait_timeout(100);
+
+               chip->card_exist |= SD_CARD;
+               do_reset_sd_card(chip);
+       }
+
+       if ((chip->int_reg & XD_EXIST) && (chip->need_reinit & XD_CARD)) {
+               release_xd_card(chip);
+
+               wait_timeout(100);
+
+               chip->card_exist |= XD_CARD;
+               do_reset_xd_card(chip);
+       }
+
+       if ((chip->int_reg & MS_EXIST) && (chip->need_reinit & MS_CARD)) {
+               release_ms_card(chip);
+
+               wait_timeout(100);
+
+               chip->card_exist |= MS_CARD;
+               do_reset_ms_card(chip);
+       }
+
+       chip->need_reinit = 0;
+}
+
+#ifdef DISABLE_CARD_INT
+void card_cd_debounce(struct rtsx_chip *chip, unsigned long *need_reset, unsigned long *need_release)
+{
+       u8 release_map = 0, reset_map = 0;
+
+       chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
+
+       if (chip->card_exist) {
+               if (chip->card_exist & XD_CARD) {
+                       if (!(chip->int_reg & XD_EXIST))
+                               release_map |= XD_CARD;
+               } else if (chip->card_exist & SD_CARD) {
+                       if (!(chip->int_reg & SD_EXIST))
+                               release_map |= SD_CARD;
+               } else if (chip->card_exist & MS_CARD) {
+                       if (!(chip->int_reg & MS_EXIST))
+                               release_map |= MS_CARD;
+               }
+       } else {
+               if (chip->int_reg & XD_EXIST)
+                       reset_map |= XD_CARD;
+               else if (chip->int_reg & SD_EXIST)
+                       reset_map |= SD_CARD;
+               else if (chip->int_reg & MS_EXIST)
+                       reset_map |= MS_CARD;
+       }
+
+       if (reset_map) {
+               int xd_cnt = 0, sd_cnt = 0, ms_cnt = 0;
+               int i;
+
+               for (i = 0; i < (DEBOUNCE_CNT); i++) {
+                       chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
+
+                       if (chip->int_reg & XD_EXIST)
+                               xd_cnt++;
+                       else
+                               xd_cnt = 0;
+
+                       if (chip->int_reg & SD_EXIST)
+                               sd_cnt++;
+                       else
+                               sd_cnt = 0;
+
+                       if (chip->int_reg & MS_EXIST)
+                               ms_cnt++;
+                       else
+                               ms_cnt = 0;
+
+                       wait_timeout(30);
+               }
+
+               reset_map = 0;
+               if (!(chip->card_exist & XD_CARD) && (xd_cnt > (DEBOUNCE_CNT-1)))
+                       reset_map |= XD_CARD;
+               if (!(chip->card_exist & SD_CARD) && (sd_cnt > (DEBOUNCE_CNT-1)))
+                       reset_map |= SD_CARD;
+               if (!(chip->card_exist & MS_CARD) && (ms_cnt > (DEBOUNCE_CNT-1)))
+                       reset_map |= MS_CARD;
+       }
+
+       if (CHECK_PID(chip, 0x5288) && CHECK_BARO_PKG(chip, QFN))
+               rtsx_write_register(chip, HOST_SLEEP_STATE, 0xC0, 0x00);
+
+       if (need_reset)
+               *need_reset = reset_map;
+       if (need_release)
+               *need_release = release_map;
+}
+#endif
+
+void rtsx_init_cards(struct rtsx_chip *chip)
+{
+       if (RTSX_TST_DELINK(chip) && (rtsx_get_stat(chip) != RTSX_STAT_SS)) {
+               RTSX_DEBUGP("Reset chip in polling thread!\n");
+               rtsx_reset_chip(chip);
+               RTSX_CLR_DELINK(chip);
+       }
+
+#ifdef DISABLE_CARD_INT
+       card_cd_debounce(chip, &(chip->need_reset), &(chip->need_release));
+#endif
+
+       if (chip->need_release) {
+               if (CHECK_PID(chip, 0x5288) && CHECK_BARO_PKG(chip, QFN)) {
+                       if (chip->int_reg & XD_EXIST) {
+                               clear_bit(SD_NR, &(chip->need_release));
+                               clear_bit(MS_NR, &(chip->need_release));
+                       }
+               }
+
+               if (!(chip->card_exist & SD_CARD) && !chip->sd_io)
+                       clear_bit(SD_NR, &(chip->need_release));
+               if (!(chip->card_exist & XD_CARD))
+                       clear_bit(XD_NR, &(chip->need_release));
+               if (!(chip->card_exist & MS_CARD))
+                       clear_bit(MS_NR, &(chip->need_release));
+
+               RTSX_DEBUGP("chip->need_release = 0x%x\n", (unsigned int)(chip->need_release));
+
+#ifdef SUPPORT_OCP
+               if (chip->need_release) {
+                       if (chip->ocp_stat & (CARD_OC_NOW | CARD_OC_EVER))
+                               rtsx_write_register(chip, OCPCLR,
+                                               CARD_OC_INT_CLR | CARD_OC_CLR,
+                                               CARD_OC_INT_CLR | CARD_OC_CLR);
+                       chip->ocp_stat = 0;
+               }
+#endif
+               if (chip->need_release) {
+                       rtsx_set_stat(chip, RTSX_STAT_RUN);
+                       rtsx_force_power_on(chip, SSC_PDCTL | OC_PDCTL);
+               }
+
+               if (chip->need_release & SD_CARD) {
+                       clear_bit(SD_NR, &(chip->need_release));
+                       chip->card_exist &= ~SD_CARD;
+                       chip->card_ejected &= ~SD_CARD;
+                       chip->card_fail &= ~SD_CARD;
+                       CLR_BIT(chip->lun_mc, chip->card2lun[SD_CARD]);
+                       chip->rw_fail_cnt[chip->card2lun[SD_CARD]] = 0;
+                       rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
+
+                       release_sdio(chip);
+                       release_sd_card(chip);
+               }
+
+               if (chip->need_release & XD_CARD) {
+                       clear_bit(XD_NR, &(chip->need_release));
+                       chip->card_exist &= ~XD_CARD;
+                       chip->card_ejected &= ~XD_CARD;
+                       chip->card_fail &= ~XD_CARD;
+                       CLR_BIT(chip->lun_mc, chip->card2lun[XD_CARD]);
+                       chip->rw_fail_cnt[chip->card2lun[XD_CARD]] = 0;
+
+                       release_xd_card(chip);
+
+                       if (CHECK_PID(chip, 0x5288) && CHECK_BARO_PKG(chip, QFN))
+                               rtsx_write_register(chip, HOST_SLEEP_STATE, 0xC0, 0xC0);
+               }
+
+               if (chip->need_release & MS_CARD) {
+                       clear_bit(MS_NR, &(chip->need_release));
+                       chip->card_exist &= ~MS_CARD;
+                       chip->card_ejected &= ~MS_CARD;
+                       chip->card_fail &= ~MS_CARD;
+                       CLR_BIT(chip->lun_mc, chip->card2lun[MS_CARD]);
+                       chip->rw_fail_cnt[chip->card2lun[MS_CARD]] = 0;
+
+                       release_ms_card(chip);
+               }
+
+               RTSX_DEBUGP("chip->card_exist = 0x%x\n", chip->card_exist);
+
+               if (!chip->card_exist)
+                       turn_off_led(chip, LED_GPIO);
+       }
+
+       if (chip->need_reset) {
+               RTSX_DEBUGP("chip->need_reset = 0x%x\n", (unsigned int)(chip->need_reset));
+
+               rtsx_reset_cards(chip);
+       }
+
+       if (chip->need_reinit) {
+               RTSX_DEBUGP("chip->need_reinit = 0x%x\n", (unsigned int)(chip->need_reinit));
+
+               rtsx_reinit_cards(chip, 0);
+       }
+}
+
+static inline u8 double_depth(u8 depth)
+{
+       return ((depth > 1) ? (depth - 1) : depth);
+}
+
+int switch_ssc_clock(struct rtsx_chip *chip, int clk)
+{
+       int retval;
+       u8 N = (u8)(clk - 2), min_N, max_N;
+       u8 mcu_cnt, div, max_div, ssc_depth, ssc_depth_mask;
+       int sd_vpclk_phase_reset = 0;
+
+       if (chip->cur_clk == clk)
+               return STATUS_SUCCESS;
+
+       min_N = 60;
+       max_N = 120;
+       max_div = CLK_DIV_4;
+
+       RTSX_DEBUGP("Switch SSC clock to %dMHz (cur_clk = %d)\n", clk, chip->cur_clk);
+
+       if ((clk <= 2) || (N > max_N))
+               TRACE_RET(chip, STATUS_FAIL);
+
+       mcu_cnt = (u8)(125/clk + 3);
+       if (mcu_cnt > 7)
+               mcu_cnt = 7;
+
+       div = CLK_DIV_1;
+       while ((N < min_N) && (div < max_div)) {
+               N = (N + 2) * 2 - 2;
+               div++;
+       }
+       RTSX_DEBUGP("N = %d, div = %d\n", N, div);
+
+       if (chip->ssc_en) {
+               ssc_depth = 0x01;
+               N -= 2;
+       } else {
+               ssc_depth = 0;
+       }
+
+       ssc_depth_mask = 0x03;
+
+       RTSX_DEBUGP("ssc_depth = %d\n", ssc_depth);
+
+       rtsx_init_cmd(chip);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, CLK_LOW_FREQ);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CLK_DIV, 0xFF, (div << 4) | mcu_cnt);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SSC_CTL2, ssc_depth_mask, ssc_depth);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, N);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB);
+       if (sd_vpclk_phase_reset) {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SD_VPCLK0_CTL, PHASE_NOT_RESET, 0);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SD_VPCLK0_CTL, PHASE_NOT_RESET, PHASE_NOT_RESET);
+       }
+
+       retval = rtsx_send_cmd(chip, 0, WAIT_TIME);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_ERROR);
+
+       udelay(10);
+       RTSX_WRITE_REG(chip, CLK_CTL, CLK_LOW_FREQ, 0);
+
+       chip->cur_clk = clk;
+
+       return STATUS_SUCCESS;
+}
+
+int switch_normal_clock(struct rtsx_chip *chip, int clk)
+{
+       u8 sel, div, mcu_cnt;
+       int sd_vpclk_phase_reset = 0;
+
+       if (chip->cur_clk == clk)
+               return STATUS_SUCCESS;
+
+       switch (clk) {
+       case CLK_20:
+               RTSX_DEBUGP("Switch clock to 20MHz\n");
+               sel = SSC_80;
+               div = CLK_DIV_4;
+               mcu_cnt = 7;
+               break;
+
+       case CLK_30:
+               RTSX_DEBUGP("Switch clock to 30MHz\n");
+               sel = SSC_120;
+               div = CLK_DIV_4;
+               mcu_cnt = 7;
+               break;
+
+       case CLK_40:
+               RTSX_DEBUGP("Switch clock to 40MHz\n");
+               sel = SSC_80;
+               div = CLK_DIV_2;
+               mcu_cnt = 7;
+               break;
+
+       case CLK_50:
+               RTSX_DEBUGP("Switch clock to 50MHz\n");
+               sel = SSC_100;
+               div = CLK_DIV_2;
+               mcu_cnt = 6;
+               break;
+
+       case CLK_60:
+               RTSX_DEBUGP("Switch clock to 60MHz\n");
+               sel = SSC_120;
+               div = CLK_DIV_2;
+               mcu_cnt = 6;
+               break;
+
+       case CLK_80:
+               RTSX_DEBUGP("Switch clock to 80MHz\n");
+               sel = SSC_80;
+               div = CLK_DIV_1;
+               mcu_cnt = 5;
+               break;
+
+       case CLK_100:
+               RTSX_DEBUGP("Switch clock to 100MHz\n");
+               sel = SSC_100;
+               div = CLK_DIV_1;
+               mcu_cnt = 5;
+               break;
+
+       case CLK_120:
+               RTSX_DEBUGP("Switch clock to 120MHz\n");
+               sel = SSC_120;
+               div = CLK_DIV_1;
+               mcu_cnt = 5;
+               break;
+
+       case CLK_150:
+               RTSX_DEBUGP("Switch clock to 150MHz\n");
+               sel = SSC_150;
+               div = CLK_DIV_1;
+               mcu_cnt = 4;
+               break;
+
+       case CLK_200:
+               RTSX_DEBUGP("Switch clock to 200MHz\n");
+               sel = SSC_200;
+               div = CLK_DIV_1;
+               mcu_cnt = 4;
+               break;
+
+       default:
+               RTSX_DEBUGP("Try to switch to an illegal clock (%d)\n", clk);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       RTSX_WRITE_REG(chip, CLK_CTL, 0xFF, CLK_LOW_FREQ);
+       if (sd_vpclk_phase_reset) {
+               RTSX_WRITE_REG(chip, SD_VPCLK0_CTL, PHASE_NOT_RESET, 0);
+               RTSX_WRITE_REG(chip, SD_VPCLK1_CTL, PHASE_NOT_RESET, 0);
+       }
+       RTSX_WRITE_REG(chip, CLK_DIV, 0xFF, (div << 4) | mcu_cnt);
+       RTSX_WRITE_REG(chip, CLK_SEL, 0xFF, sel);
+
+       if (sd_vpclk_phase_reset) {
+               udelay(200);
+               RTSX_WRITE_REG(chip, SD_VPCLK0_CTL, PHASE_NOT_RESET, PHASE_NOT_RESET);
+               RTSX_WRITE_REG(chip, SD_VPCLK1_CTL, PHASE_NOT_RESET, PHASE_NOT_RESET);
+               udelay(200);
+       }
+       RTSX_WRITE_REG(chip, CLK_CTL, 0xFF, 0);
+
+       chip->cur_clk = clk;
+
+       return STATUS_SUCCESS;
+}
+
+void trans_dma_enable(enum dma_data_direction dir, struct rtsx_chip *chip, u32 byte_cnt, u8 pack_size)
+{
+       if (pack_size > DMA_1024)
+               pack_size = DMA_512;
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, IRQSTAT0, DMA_DONE_INT, DMA_DONE_INT);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, DMATC3, 0xFF, (u8)(byte_cnt >> 24));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, DMATC2, 0xFF, (u8)(byte_cnt >> 16));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, DMATC1, 0xFF, (u8)(byte_cnt >> 8));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, DMATC0, 0xFF, (u8)byte_cnt);
+
+       if (dir == DMA_FROM_DEVICE) {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, DMACTL, 0x03 | DMA_PACK_SIZE_MASK,
+                            DMA_DIR_FROM_CARD | DMA_EN | pack_size);
+       } else {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, DMACTL, 0x03 | DMA_PACK_SIZE_MASK,
+                            DMA_DIR_TO_CARD | DMA_EN | pack_size);
+       }
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+}
+
+int enable_card_clock(struct rtsx_chip *chip, u8 card)
+{
+       u8 clk_en = 0;
+
+       if (card & XD_CARD)
+               clk_en |= XD_CLK_EN;
+       if (card & SD_CARD)
+               clk_en |= SD_CLK_EN;
+       if (card & MS_CARD)
+               clk_en |= MS_CLK_EN;
+
+       RTSX_WRITE_REG(chip, CARD_CLK_EN, clk_en, clk_en);
+
+       return STATUS_SUCCESS;
+}
+
+int disable_card_clock(struct rtsx_chip *chip, u8 card)
+{
+       u8 clk_en = 0;
+
+       if (card & XD_CARD)
+               clk_en |= XD_CLK_EN;
+       if (card & SD_CARD)
+               clk_en |= SD_CLK_EN;
+       if (card & MS_CARD)
+               clk_en |= MS_CLK_EN;
+
+       RTSX_WRITE_REG(chip, CARD_CLK_EN, clk_en, 0);
+
+       return STATUS_SUCCESS;
+}
+
+int card_power_on(struct rtsx_chip *chip, u8 card)
+{
+       int retval;
+       u8 mask, val1, val2;
+
+       if (CHECK_LUN_MODE(chip, SD_MS_2LUN) && (card == MS_CARD)) {
+               mask = MS_POWER_MASK;
+               val1 = MS_PARTIAL_POWER_ON;
+               val2 = MS_POWER_ON;
+       } else {
+               mask = SD_POWER_MASK;
+               val1 = SD_PARTIAL_POWER_ON;
+               val2 = SD_POWER_ON;
+       }
+
+       rtsx_init_cmd(chip);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PWR_CTL, mask, val1);
+
+       retval = rtsx_send_cmd(chip, 0, 100);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       udelay(chip->pmos_pwr_on_interval);
+
+       rtsx_init_cmd(chip);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PWR_CTL, mask, val2);
+
+       retval = rtsx_send_cmd(chip, 0, 100);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+int card_power_off(struct rtsx_chip *chip, u8 card)
+{
+       u8 mask, val;
+
+       if (CHECK_LUN_MODE(chip, SD_MS_2LUN) && (card == MS_CARD)) {
+               mask = MS_POWER_MASK;
+               val = MS_POWER_OFF;
+       } else {
+               mask = SD_POWER_MASK;
+               val = SD_POWER_OFF;
+       }
+
+       RTSX_WRITE_REG(chip, CARD_PWR_CTL, mask, val);
+
+       return STATUS_SUCCESS;
+}
+
+int card_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 sec_addr, u16 sec_cnt)
+{
+       int retval;
+       unsigned int lun = SCSI_LUN(srb);
+       int i;
+
+       if (chip->rw_card[lun] == NULL)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       for (i = 0; i < 3; i++) {
+               chip->rw_need_retry = 0;
+
+               retval = chip->rw_card[lun](srb, chip, sec_addr, sec_cnt);
+               if (retval != STATUS_SUCCESS) {
+                       if (rtsx_check_chip_exist(chip) != STATUS_SUCCESS) {
+                               rtsx_release_chip(chip);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       if (detect_card_cd(chip, chip->cur_card) != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       if (!chip->rw_need_retry) {
+                               RTSX_DEBUGP("RW fail, but no need to retry\n");
+                               break;
+                       }
+               } else {
+                       chip->rw_need_retry = 0;
+                       break;
+               }
+
+               RTSX_DEBUGP("Retry RW, (i = %d)\n", i);
+       }
+
+       return retval;
+}
+
+int card_share_mode(struct rtsx_chip *chip, int card)
+{
+       u8 mask, value;
+
+       if (CHECK_PID(chip, 0x5208)) {
+               mask = CARD_SHARE_MASK;
+               if (card == SD_CARD)
+                       value = CARD_SHARE_48_SD;
+               else if (card == MS_CARD)
+                       value = CARD_SHARE_48_MS;
+               else if (card == XD_CARD)
+                       value = CARD_SHARE_48_XD;
+               else
+                       TRACE_RET(chip, STATUS_FAIL);
+
+       } else if (CHECK_PID(chip, 0x5288)) {
+               mask = 0x03;
+               if (card == SD_CARD)
+                       value = CARD_SHARE_BAROSSA_SD;
+               else if (card == MS_CARD)
+                       value = CARD_SHARE_BAROSSA_MS;
+               else if (card == XD_CARD)
+                       value = CARD_SHARE_BAROSSA_XD;
+               else
+                       TRACE_RET(chip, STATUS_FAIL);
+
+       } else {
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       RTSX_WRITE_REG(chip, CARD_SHARE_MODE, mask, value);
+
+       return STATUS_SUCCESS;
+}
+
+
+int select_card(struct rtsx_chip *chip, int card)
+{
+       int retval;
+
+       if (chip->cur_card != card) {
+               u8 mod;
+
+               if (card == SD_CARD)
+                       mod = SD_MOD_SEL;
+               else if (card == MS_CARD)
+                       mod = MS_MOD_SEL;
+               else if (card == XD_CARD)
+                       mod = XD_MOD_SEL;
+               else if (card == SPI_CARD)
+                       mod = SPI_MOD_SEL;
+               else
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               RTSX_WRITE_REG(chip, CARD_SELECT, 0x07, mod);
+               chip->cur_card = card;
+
+               retval =  card_share_mode(chip, card);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+void toggle_gpio(struct rtsx_chip *chip, u8 gpio)
+{
+       u8 temp_reg;
+
+       rtsx_read_register(chip, CARD_GPIO, &temp_reg);
+       temp_reg ^= (0x01 << gpio);
+       rtsx_write_register(chip, CARD_GPIO, 0xFF, temp_reg);
+}
+
+void turn_on_led(struct rtsx_chip *chip, u8 gpio)
+{
+       if (CHECK_PID(chip, 0x5288))
+               rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), (u8)(1 << gpio));
+       else
+               rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), 0);
+}
+
+void turn_off_led(struct rtsx_chip *chip, u8 gpio)
+{
+       if (CHECK_PID(chip, 0x5288))
+               rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), 0);
+       else
+               rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), (u8)(1 << gpio));
+}
+
+int detect_card_cd(struct rtsx_chip *chip, int card)
+{
+       u32 card_cd, status;
+
+       if (card == SD_CARD) {
+               card_cd = SD_EXIST;
+       } else if (card == MS_CARD) {
+               card_cd = MS_EXIST;
+       } else if (card == XD_CARD) {
+               card_cd = XD_EXIST;
+       } else {
+               RTSX_DEBUGP("Wrong card type: 0x%x\n", card);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       status = rtsx_readl(chip, RTSX_BIPR);
+       if (!(status & card_cd))
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+int check_card_exist(struct rtsx_chip *chip, unsigned int lun)
+{
+       if (chip->card_exist & chip->lun2card[lun])
+               return 1;
+
+       return 0;
+}
+
+int check_card_ready(struct rtsx_chip *chip, unsigned int lun)
+{
+       if (chip->card_ready & chip->lun2card[lun])
+               return 1;
+
+       return 0;
+}
+
+int check_card_wp(struct rtsx_chip *chip, unsigned int lun)
+{
+       if (chip->card_wp & chip->lun2card[lun])
+               return 1;
+
+       return 0;
+}
+
+int check_card_fail(struct rtsx_chip *chip, unsigned int lun)
+{
+       if (chip->card_fail & chip->lun2card[lun])
+               return 1;
+
+       return 0;
+}
+
+int check_card_ejected(struct rtsx_chip *chip, unsigned int lun)
+{
+       if (chip->card_ejected & chip->lun2card[lun])
+               return 1;
+
+       return 0;
+}
+
+u8 get_lun_card(struct rtsx_chip *chip, unsigned int lun)
+{
+       if ((chip->card_ready & chip->lun2card[lun]) == XD_CARD)
+               return (u8)XD_CARD;
+       else if ((chip->card_ready & chip->lun2card[lun]) == SD_CARD)
+               return (u8)SD_CARD;
+       else if ((chip->card_ready & chip->lun2card[lun]) == MS_CARD)
+               return (u8)MS_CARD;
+
+       return 0;
+}
+
+void eject_card(struct rtsx_chip *chip, unsigned int lun)
+{
+       do_remaining_work(chip);
+
+       if ((chip->card_ready & chip->lun2card[lun]) == SD_CARD) {
+               release_sd_card(chip);
+               chip->card_ejected |= SD_CARD;
+               chip->card_ready &= ~SD_CARD;
+               chip->capacity[lun] = 0;
+       } else if ((chip->card_ready & chip->lun2card[lun]) == XD_CARD) {
+               release_xd_card(chip);
+               chip->card_ejected |= XD_CARD;
+               chip->card_ready &= ~XD_CARD;
+               chip->capacity[lun] = 0;
+       } else if ((chip->card_ready & chip->lun2card[lun]) == MS_CARD) {
+               release_ms_card(chip);
+               chip->card_ejected |= MS_CARD;
+               chip->card_ready &= ~MS_CARD;
+               chip->capacity[lun] = 0;
+       }
+}
diff --git a/drivers/staging/rts5208/rtsx_card.h b/drivers/staging/rts5208/rtsx_card.h
new file mode 100644 (file)
index 0000000..4528b61
--- /dev/null
@@ -0,0 +1,1098 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_CARD_H
+#define __REALTEK_RTSX_CARD_H
+
+#include "debug.h"
+#include "rtsx.h"
+#include "rtsx_chip.h"
+#include "rtsx_transport.h"
+#include "sd.h"
+
+#define SSC_POWER_DOWN         0x01
+#define SD_OC_POWER_DOWN       0x02
+#define MS_OC_POWER_DOWN       0x04
+#define ALL_POWER_DOWN         0x07
+#define OC_POWER_DOWN          0x06
+
+#define PMOS_STRG_MASK         0x10
+#define PMOS_STRG_800mA                0x10
+#define PMOS_STRG_400mA                0x00
+
+#define POWER_OFF              0x03
+#define PARTIAL_POWER_ON       0x01
+#define POWER_ON               0x00
+
+#define MS_POWER_OFF           0x0C
+#define MS_PARTIAL_POWER_ON    0x04
+#define MS_POWER_ON            0x00
+#define MS_POWER_MASK          0x0C
+
+#define SD_POWER_OFF           0x03
+#define SD_PARTIAL_POWER_ON    0x01
+#define SD_POWER_ON            0x00
+#define SD_POWER_MASK          0x03
+
+#define XD_OUTPUT_EN           0x02
+#define SD_OUTPUT_EN           0x04
+#define MS_OUTPUT_EN           0x08
+#define SPI_OUTPUT_EN          0x10
+
+#define CLK_LOW_FREQ           0x01
+
+#define CLK_DIV_1              0x01
+#define CLK_DIV_2              0x02
+#define CLK_DIV_4              0x03
+#define CLK_DIV_8              0x04
+
+#define SSC_80                 0
+#define SSC_100                        1
+#define SSC_120                        2
+#define SSC_150                        3
+#define SSC_200                        4
+
+#define XD_CLK_EN              0x02
+#define SD_CLK_EN              0x04
+#define MS_CLK_EN              0x08
+#define SPI_CLK_EN             0x10
+
+#define XD_MOD_SEL             1
+#define SD_MOD_SEL             2
+#define MS_MOD_SEL             3
+#define SPI_MOD_SEL            4
+
+#define CHANGE_CLK             0x01
+
+#define        SD_CRC7_ERR                     0x80
+#define        SD_CRC16_ERR                    0x40
+#define        SD_CRC_WRITE_ERR                0x20
+#define        SD_CRC_WRITE_ERR_MASK           0x1C
+#define        GET_CRC_TIME_OUT                0x02
+#define        SD_TUNING_COMPARE_ERR           0x01
+
+#define        SD_RSP_80CLK_TIMEOUT            0x01
+
+#define        SD_CLK_TOGGLE_EN                0x80
+#define        SD_CLK_FORCE_STOP               0x40
+#define        SD_DAT3_STATUS                  0x10
+#define        SD_DAT2_STATUS                  0x08
+#define        SD_DAT1_STATUS                  0x04
+#define        SD_DAT0_STATUS                  0x02
+#define        SD_CMD_STATUS                   0x01
+
+#define        SD_IO_USING_1V8                 0x80
+#define        SD_IO_USING_3V3                 0x7F
+#define        TYPE_A_DRIVING                  0x00
+#define        TYPE_B_DRIVING                  0x01
+#define        TYPE_C_DRIVING                  0x02
+#define        TYPE_D_DRIVING                  0x03
+
+#define        DDR_FIX_RX_DAT                  0x00
+#define        DDR_VAR_RX_DAT                  0x80
+#define        DDR_FIX_RX_DAT_EDGE             0x00
+#define        DDR_FIX_RX_DAT_14_DELAY         0x40
+#define        DDR_FIX_RX_CMD                  0x00
+#define        DDR_VAR_RX_CMD                  0x20
+#define        DDR_FIX_RX_CMD_POS_EDGE         0x00
+#define        DDR_FIX_RX_CMD_14_DELAY         0x10
+#define        SD20_RX_POS_EDGE                0x00
+#define        SD20_RX_14_DELAY                0x08
+#define SD20_RX_SEL_MASK               0x08
+
+#define        DDR_FIX_TX_CMD_DAT              0x00
+#define        DDR_VAR_TX_CMD_DAT              0x80
+#define        DDR_FIX_TX_DAT_14_TSU           0x00
+#define        DDR_FIX_TX_DAT_12_TSU           0x40
+#define        DDR_FIX_TX_CMD_NEG_EDGE         0x00
+#define        DDR_FIX_TX_CMD_14_AHEAD         0x20
+#define        SD20_TX_NEG_EDGE                0x00
+#define        SD20_TX_14_AHEAD                0x10
+#define SD20_TX_SEL_MASK               0x10
+#define        DDR_VAR_SDCLK_POL_SWAP          0x01
+
+#define        SD_TRANSFER_START               0x80
+#define        SD_TRANSFER_END                 0x40
+#define SD_STAT_IDLE                   0x20
+#define        SD_TRANSFER_ERR                 0x10
+#define        SD_TM_NORMAL_WRITE              0x00
+#define        SD_TM_AUTO_WRITE_3              0x01
+#define        SD_TM_AUTO_WRITE_4              0x02
+#define        SD_TM_AUTO_READ_3               0x05
+#define        SD_TM_AUTO_READ_4               0x06
+#define        SD_TM_CMD_RSP                   0x08
+#define        SD_TM_AUTO_WRITE_1              0x09
+#define        SD_TM_AUTO_WRITE_2              0x0A
+#define        SD_TM_NORMAL_READ               0x0C
+#define        SD_TM_AUTO_READ_1               0x0D
+#define        SD_TM_AUTO_READ_2               0x0E
+#define        SD_TM_AUTO_TUNING               0x0F
+
+#define PHASE_CHANGE                   0x80
+#define PHASE_NOT_RESET                        0x40
+
+#define DCMPS_CHANGE                   0x80
+#define DCMPS_CHANGE_DONE              0x40
+#define DCMPS_ERROR                    0x20
+#define DCMPS_CURRENT_PHASE            0x1F
+
+#define SD_CLK_DIVIDE_0                        0x00
+#define        SD_CLK_DIVIDE_256               0xC0
+#define        SD_CLK_DIVIDE_128               0x80
+#define        SD_BUS_WIDTH_1                  0x00
+#define        SD_BUS_WIDTH_4                  0x01
+#define        SD_BUS_WIDTH_8                  0x02
+#define        SD_ASYNC_FIFO_NOT_RST           0x10
+#define        SD_20_MODE                      0x00
+#define        SD_DDR_MODE                     0x04
+#define        SD_30_MODE                      0x08
+
+#define SD_CLK_DIVIDE_MASK             0xC0
+
+#define SD_CMD_IDLE                    0x80
+
+#define SD_DATA_IDLE                   0x80
+
+#define DCM_RESET                      0x08
+#define DCM_LOCKED                     0x04
+#define DCM_208M                       0x00
+#define DCM_TX                         0x01
+#define DCM_RX                         0x02
+
+#define DRP_START                      0x80
+#define DRP_DONE                       0x40
+
+#define DRP_WRITE                      0x80
+#define DRP_READ                       0x00
+#define DCM_WRITE_ADDRESS_50           0x50
+#define DCM_WRITE_ADDRESS_51           0x51
+#define DCM_READ_ADDRESS_00            0x00
+#define DCM_READ_ADDRESS_51            0x51
+
+#define        SD_CALCULATE_CRC7               0x00
+#define        SD_NO_CALCULATE_CRC7            0x80
+#define        SD_CHECK_CRC16                  0x00
+#define        SD_NO_CHECK_CRC16               0x40
+#define SD_NO_CHECK_WAIT_CRC_TO                0x20
+#define        SD_WAIT_BUSY_END                0x08
+#define        SD_NO_WAIT_BUSY_END             0x00
+#define        SD_CHECK_CRC7                   0x00
+#define        SD_NO_CHECK_CRC7                0x04
+#define        SD_RSP_LEN_0                    0x00
+#define        SD_RSP_LEN_6                    0x01
+#define        SD_RSP_LEN_17                   0x02
+#define        SD_RSP_TYPE_R0                  0x04
+#define        SD_RSP_TYPE_R1                  0x01
+#define        SD_RSP_TYPE_R1b                 0x09
+#define        SD_RSP_TYPE_R2                  0x02
+#define        SD_RSP_TYPE_R3                  0x05
+#define        SD_RSP_TYPE_R4                  0x05
+#define        SD_RSP_TYPE_R5                  0x01
+#define        SD_RSP_TYPE_R6                  0x01
+#define        SD_RSP_TYPE_R7                  0x01
+
+#define        SD_RSP_80CLK_TIMEOUT_EN         0x01
+
+#define        SAMPLE_TIME_RISING              0x00
+#define        SAMPLE_TIME_FALLING             0x80
+#define        PUSH_TIME_DEFAULT               0x00
+#define        PUSH_TIME_ODD                   0x40
+#define        NO_EXTEND_TOGGLE                0x00
+#define        EXTEND_TOGGLE_CHK               0x20
+#define        MS_BUS_WIDTH_1                  0x00
+#define        MS_BUS_WIDTH_4                  0x10
+#define        MS_BUS_WIDTH_8                  0x18
+#define        MS_2K_SECTOR_MODE               0x04
+#define        MS_512_SECTOR_MODE              0x00
+#define        MS_TOGGLE_TIMEOUT_EN            0x00
+#define        MS_TOGGLE_TIMEOUT_DISEN         0x01
+#define MS_NO_CHECK_INT                        0x02
+
+#define        WAIT_INT                        0x80
+#define        NO_WAIT_INT                     0x00
+#define        NO_AUTO_READ_INT_REG            0x00
+#define        AUTO_READ_INT_REG               0x40
+#define        MS_CRC16_ERR                    0x20
+#define        MS_RDY_TIMEOUT                  0x10
+#define        MS_INT_CMDNK                    0x08
+#define        MS_INT_BREQ                     0x04
+#define        MS_INT_ERR                      0x02
+#define        MS_INT_CED                      0x01
+
+#define        MS_TRANSFER_START               0x80
+#define        MS_TRANSFER_END                 0x40
+#define        MS_TRANSFER_ERR                 0x20
+#define        MS_BS_STATE                     0x10
+#define        MS_TM_READ_BYTES                0x00
+#define        MS_TM_NORMAL_READ               0x01
+#define        MS_TM_WRITE_BYTES               0x04
+#define        MS_TM_NORMAL_WRITE              0x05
+#define        MS_TM_AUTO_READ                 0x08
+#define        MS_TM_AUTO_WRITE                0x0C
+
+#define CARD_SHARE_MASK                        0x0F
+#define CARD_SHARE_MULTI_LUN           0x00
+#define        CARD_SHARE_NORMAL               0x00
+#define        CARD_SHARE_48_XD                0x02
+#define        CARD_SHARE_48_SD                0x04
+#define        CARD_SHARE_48_MS                0x08
+#define CARD_SHARE_BAROSSA_XD          0x00
+#define CARD_SHARE_BAROSSA_SD          0x01
+#define CARD_SHARE_BAROSSA_MS          0x02
+
+#define        MS_DRIVE_8                      0x00
+#define        MS_DRIVE_4                      0x40
+#define        MS_DRIVE_12                     0x80
+#define        SD_DRIVE_8                      0x00
+#define        SD_DRIVE_4                      0x10
+#define        SD_DRIVE_12                     0x20
+#define        XD_DRIVE_8                      0x00
+#define        XD_DRIVE_4                      0x04
+#define        XD_DRIVE_12                     0x08
+
+#define SPI_STOP               0x01
+#define XD_STOP                        0x02
+#define SD_STOP                        0x04
+#define MS_STOP                        0x08
+#define SPI_CLR_ERR            0x10
+#define XD_CLR_ERR             0x20
+#define SD_CLR_ERR             0x40
+#define MS_CLR_ERR             0x80
+
+#define CRC_FIX_CLK            (0x00 << 0)
+#define CRC_VAR_CLK0           (0x01 << 0)
+#define CRC_VAR_CLK1           (0x02 << 0)
+#define SD30_FIX_CLK           (0x00 << 2)
+#define SD30_VAR_CLK0          (0x01 << 2)
+#define SD30_VAR_CLK1          (0x02 << 2)
+#define SAMPLE_FIX_CLK         (0x00 << 4)
+#define SAMPLE_VAR_CLK0                (0x01 << 4)
+#define SAMPLE_VAR_CLK1                (0x02 << 4)
+
+#define SDIO_VER_20            0x80
+#define SDIO_VER_10            0x00
+#define SDIO_VER_CHG           0x40
+#define SDIO_BUS_AUTO_SWITCH   0x10
+
+#define PINGPONG_BUFFER                0x01
+#define RING_BUFFER            0x00
+
+#define RB_FLUSH               0x80
+
+#define DMA_DONE_INT_EN                        0x80
+#define SUSPEND_INT_EN                 0x40
+#define LINK_RDY_INT_EN                        0x20
+#define LINK_DOWN_INT_EN               0x10
+
+#define DMA_DONE_INT                   0x80
+#define SUSPEND_INT                    0x40
+#define LINK_RDY_INT                   0x20
+#define LINK_DOWN_INT                  0x10
+
+#define MRD_ERR_INT_EN                 0x40
+#define MWR_ERR_INT_EN                 0x20
+#define SCSI_CMD_INT_EN                        0x10
+#define TLP_RCV_INT_EN                 0x08
+#define TLP_TRSMT_INT_EN               0x04
+#define MRD_COMPLETE_INT_EN            0x02
+#define MWR_COMPLETE_INT_EN            0x01
+
+#define MRD_ERR_INT                    0x40
+#define MWR_ERR_INT                    0x20
+#define SCSI_CMD_INT                   0x10
+#define TLP_RX_INT                     0x08
+#define TLP_TX_INT                     0x04
+#define MRD_COMPLETE_INT               0x02
+#define MWR_COMPLETE_INT               0x01
+
+#define MSG_RX_INT_EN                  0x08
+#define MRD_RX_INT_EN                  0x04
+#define MWR_RX_INT_EN                  0x02
+#define CPLD_RX_INT_EN                 0x01
+
+#define MSG_RX_INT                     0x08
+#define MRD_RX_INT                     0x04
+#define MWR_RX_INT                     0x02
+#define CPLD_RX_INT                    0x01
+
+#define MSG_TX_INT_EN                  0x08
+#define MRD_TX_INT_EN                  0x04
+#define MWR_TX_INT_EN                  0x02
+#define CPLD_TX_INT_EN                 0x01
+
+#define MSG_TX_INT                     0x08
+#define MRD_TX_INT                     0x04
+#define MWR_TX_INT                     0x02
+#define CPLD_TX_INT                    0x01
+
+#define DMA_RST                                0x80
+#define DMA_BUSY                       0x04
+#define DMA_DIR_TO_CARD                        0x00
+#define DMA_DIR_FROM_CARD              0x02
+#define DMA_EN                         0x01
+#define DMA_128                                (0 << 4)
+#define DMA_256                                (1 << 4)
+#define DMA_512                                (2 << 4)
+#define DMA_1024                       (3 << 4)
+#define DMA_PACK_SIZE_MASK             0x30
+
+#define        XD_PWR_OFF_DELAY0               0x00
+#define        XD_PWR_OFF_DELAY1               0x02
+#define        XD_PWR_OFF_DELAY2               0x04
+#define        XD_PWR_OFF_DELAY3               0x06
+#define        XD_AUTO_PWR_OFF_EN              0xF7
+#define        XD_NO_AUTO_PWR_OFF              0x08
+
+#define        XD_TIME_RWN_1                   0x00
+#define        XD_TIME_RWN_STEP                0x20
+#define        XD_TIME_RW_1                    0x00
+#define        XD_TIME_RW_STEP                 0x04
+#define        XD_TIME_SETUP_1                 0x00
+#define        XD_TIME_SETUP_STEP              0x01
+
+#define        XD_ECC2_UNCORRECTABLE           0x80
+#define        XD_ECC2_ERROR                   0x40
+#define        XD_ECC1_UNCORRECTABLE           0x20
+#define        XD_ECC1_ERROR                   0x10
+#define        XD_RDY                          0x04
+#define        XD_CE_EN                        0xFD
+#define        XD_CE_DISEN                     0x02
+#define        XD_WP_EN                        0xFE
+#define        XD_WP_DISEN                     0x01
+
+#define        XD_TRANSFER_START               0x80
+#define        XD_TRANSFER_END                 0x40
+#define        XD_PPB_EMPTY                    0x20
+#define        XD_RESET                        0x00
+#define        XD_ERASE                        0x01
+#define        XD_READ_STATUS                  0x02
+#define        XD_READ_ID                      0x03
+#define        XD_READ_REDUNDANT               0x04
+#define        XD_READ_PAGES                   0x05
+#define        XD_SET_CMD                      0x06
+#define        XD_NORMAL_READ                  0x07
+#define        XD_WRITE_PAGES                  0x08
+#define        XD_NORMAL_WRITE                 0x09
+#define        XD_WRITE_REDUNDANT              0x0A
+#define        XD_SET_ADDR                     0x0B
+
+#define        XD_PPB_TO_SIE                   0x80
+#define        XD_TO_PPB_ONLY                  0x00
+#define        XD_BA_TRANSFORM                 0x40
+#define        XD_BA_NO_TRANSFORM              0x00
+#define        XD_NO_CALC_ECC                  0x20
+#define        XD_CALC_ECC                     0x00
+#define        XD_IGNORE_ECC                   0x10
+#define        XD_CHECK_ECC                    0x00
+#define        XD_DIRECT_TO_RB                 0x08
+#define        XD_ADDR_LENGTH_0                0x00
+#define        XD_ADDR_LENGTH_1                0x01
+#define        XD_ADDR_LENGTH_2                0x02
+#define        XD_ADDR_LENGTH_3                0x03
+#define        XD_ADDR_LENGTH_4                0x04
+
+#define        XD_GPG                          0xFF
+#define        XD_BPG                          0x00
+
+#define        XD_GBLK                         0xFF
+#define        XD_LATER_BBLK                   0xF0
+
+#define        XD_ECC2_ALL1                    0x80
+#define        XD_ECC1_ALL1                    0x40
+#define        XD_BA2_ALL0                     0x20
+#define        XD_BA1_ALL0                     0x10
+#define        XD_BA1_BA2_EQL                  0x04
+#define        XD_BA2_VALID                    0x02
+#define        XD_BA1_VALID                    0x01
+
+#define        XD_PGSTS_ZEROBIT_OVER4          0x00
+#define        XD_PGSTS_NOT_FF                 0x02
+#define        XD_AUTO_CHK_DATA_STATUS         0x01
+
+#define        RSTB_MODE_DETECT                0x80
+#define        MODE_OUT_VLD                    0x40
+#define        MODE_OUT_0_NONE                 0x00
+#define        MODE_OUT_10_NONE                0x04
+#define        MODE_OUT_10_47                  0x05
+#define        MODE_OUT_10_180                 0x06
+#define        MODE_OUT_10_680                 0x07
+#define        MODE_OUT_16_NONE                0x08
+#define        MODE_OUT_16_47                  0x09
+#define        MODE_OUT_16_180                 0x0A
+#define        MODE_OUT_16_680                 0x0B
+#define        MODE_OUT_NONE_NONE              0x0C
+#define        MODE_OUT_NONE_47                0x0D
+#define        MODE_OUT_NONE_180               0x0E
+#define        MODE_OUT_NONE_680               0x0F
+
+#define        CARD_OC_INT_EN                  0x20
+#define        CARD_DETECT_EN                  0x08
+
+#define MS_DETECT_EN                   0x80
+#define MS_OCP_INT_EN                  0x40
+#define MS_OCP_INT_CLR                 0x20
+#define MS_OC_CLR                      0x10
+#define SD_DETECT_EN                   0x08
+#define SD_OCP_INT_EN                  0x04
+#define SD_OCP_INT_CLR                 0x02
+#define SD_OC_CLR                      0x01
+
+#define        CARD_OCP_DETECT                 0x80
+#define        CARD_OC_NOW                     0x08
+#define        CARD_OC_EVER                    0x04
+
+#define MS_OCP_DETECT                  0x80
+#define MS_OC_NOW                      0x40
+#define MS_OC_EVER                     0x20
+#define SD_OCP_DETECT                  0x08
+#define SD_OC_NOW                      0x04
+#define SD_OC_EVER                     0x02
+
+#define        CARD_OC_INT_CLR                 0x08
+#define        CARD_OC_CLR                     0x02
+
+#define SD_OCP_GLITCH_MASK             0x07
+#define SD_OCP_GLITCH_6_4              0x00
+#define SD_OCP_GLITCH_64               0x01
+#define SD_OCP_GLITCH_640              0x02
+#define SD_OCP_GLITCH_1000             0x03
+#define SD_OCP_GLITCH_2000             0x04
+#define SD_OCP_GLITCH_4000             0x05
+#define SD_OCP_GLITCH_8000             0x06
+#define SD_OCP_GLITCH_10000            0x07
+
+#define MS_OCP_GLITCH_MASK             0x70
+#define MS_OCP_GLITCH_6_4              (0x00 << 4)
+#define MS_OCP_GLITCH_64               (0x01 << 4)
+#define MS_OCP_GLITCH_640              (0x02 << 4)
+#define MS_OCP_GLITCH_1000             (0x03 << 4)
+#define MS_OCP_GLITCH_2000             (0x04 << 4)
+#define MS_OCP_GLITCH_4000             (0x05 << 4)
+#define MS_OCP_GLITCH_8000             (0x06 << 4)
+#define MS_OCP_GLITCH_10000            (0x07 << 4)
+
+#define OCP_TIME_60                    0x00
+#define OCP_TIME_100                   (0x01 << 3)
+#define OCP_TIME_200                   (0x02 << 3)
+#define OCP_TIME_400                   (0x03 << 3)
+#define OCP_TIME_600                   (0x04 << 3)
+#define OCP_TIME_800                   (0x05 << 3)
+#define OCP_TIME_1100                  (0x06 << 3)
+#define OCP_TIME_MASK                  0x38
+
+#define MS_OCP_TIME_60                 0x00
+#define MS_OCP_TIME_100                        (0x01 << 4)
+#define MS_OCP_TIME_200                        (0x02 << 4)
+#define MS_OCP_TIME_400                        (0x03 << 4)
+#define MS_OCP_TIME_600                        (0x04 << 4)
+#define MS_OCP_TIME_800                        (0x05 << 4)
+#define MS_OCP_TIME_1100               (0x06 << 4)
+#define MS_OCP_TIME_MASK               0x70
+
+#define SD_OCP_TIME_60                 0x00
+#define SD_OCP_TIME_100                        0x01
+#define SD_OCP_TIME_200                        0x02
+#define SD_OCP_TIME_400                        0x03
+#define SD_OCP_TIME_600                        0x04
+#define SD_OCP_TIME_800                        0x05
+#define SD_OCP_TIME_1100               0x06
+#define SD_OCP_TIME_MASK               0x07
+
+#define OCP_THD_315_417                        0x00
+#define OCP_THD_283_783                        (0x01 << 6)
+#define OCP_THD_244_946                        (0x02 << 6)
+#define OCP_THD_191_1080               (0x03 << 6)
+#define OCP_THD_MASK                   0xC0
+
+#define MS_OCP_THD_450                 0x00
+#define MS_OCP_THD_550                 (0x01 << 4)
+#define MS_OCP_THD_650                 (0x02 << 4)
+#define MS_OCP_THD_750                 (0x03 << 4)
+#define MS_OCP_THD_850                 (0x04 << 4)
+#define MS_OCP_THD_950                 (0x05 << 4)
+#define MS_OCP_THD_1050                        (0x06 << 4)
+#define MS_OCP_THD_1150                        (0x07 << 4)
+#define MS_OCP_THD_MASK                        0x70
+
+#define SD_OCP_THD_450                 0x00
+#define SD_OCP_THD_550                 0x01
+#define SD_OCP_THD_650                 0x02
+#define SD_OCP_THD_750                 0x03
+#define SD_OCP_THD_850                 0x04
+#define SD_OCP_THD_950                 0x05
+#define SD_OCP_THD_1050                        0x06
+#define SD_OCP_THD_1150                        0x07
+#define SD_OCP_THD_MASK                        0x07
+
+#define FPGA_MS_PULL_CTL_EN            0xEF
+#define FPGA_SD_PULL_CTL_EN            0xF7
+#define FPGA_XD_PULL_CTL_EN1           0xFE
+#define FPGA_XD_PULL_CTL_EN2           0xFD
+#define FPGA_XD_PULL_CTL_EN3           0xFB
+
+#define FPGA_MS_PULL_CTL_BIT           0x10
+#define FPGA_SD_PULL_CTL_BIT           0x08
+
+#define BLINK_EN                       0x08
+#define LED_GPIO0                      (0 << 4)
+#define LED_GPIO1                      (1 << 4)
+#define LED_GPIO2                      (2 << 4)
+
+#define SDIO_BUS_CTRL          0x01
+#define SDIO_CD_CTRL           0x02
+
+#define SSC_RSTB               0x80
+#define SSC_8X_EN              0x40
+#define SSC_FIX_FRAC           0x20
+#define SSC_SEL_1M             0x00
+#define SSC_SEL_2M             0x08
+#define SSC_SEL_4M             0x10
+#define SSC_SEL_8M             0x18
+
+#define SSC_DEPTH_MASK         0x07
+#define SSC_DEPTH_DISALBE      0x00
+#define SSC_DEPTH_4M           0x01
+#define SSC_DEPTH_2M           0x02
+#define SSC_DEPTH_1M           0x03
+#define SSC_DEPTH_512K         0x04
+#define SSC_DEPTH_256K         0x05
+#define SSC_DEPTH_128K         0x06
+#define SSC_DEPTH_64K          0x07
+
+#define XD_D3_NP               0x00
+#define XD_D3_PD               (0x01 << 6)
+#define XD_D3_PU               (0x02 << 6)
+#define XD_D2_NP               0x00
+#define XD_D2_PD               (0x01 << 4)
+#define XD_D2_PU               (0x02 << 4)
+#define XD_D1_NP               0x00
+#define XD_D1_PD               (0x01 << 2)
+#define XD_D1_PU               (0x02 << 2)
+#define XD_D0_NP               0x00
+#define XD_D0_PD               0x01
+#define XD_D0_PU               0x02
+
+#define SD_D7_NP               0x00
+#define SD_D7_PD               (0x01 << 4)
+#define SD_DAT7_PU             (0x02 << 4)
+#define SD_CLK_NP              0x00
+#define SD_CLK_PD              (0x01 << 2)
+#define SD_CLK_PU              (0x02 << 2)
+#define SD_D5_NP               0x00
+#define SD_D5_PD               0x01
+#define SD_D5_PU               0x02
+
+#define MS_D1_NP               0x00
+#define MS_D1_PD               (0x01 << 6)
+#define MS_D1_PU               (0x02 << 6)
+#define MS_D2_NP               0x00
+#define MS_D2_PD               (0x01 << 4)
+#define MS_D2_PU               (0x02 << 4)
+#define MS_CLK_NP              0x00
+#define MS_CLK_PD              (0x01 << 2)
+#define MS_CLK_PU              (0x02 << 2)
+#define MS_D6_NP               0x00
+#define MS_D6_PD               0x01
+#define MS_D6_PU               0x02
+
+#define XD_D7_NP               0x00
+#define XD_D7_PD               (0x01 << 6)
+#define XD_D7_PU               (0x02 << 6)
+#define XD_D6_NP               0x00
+#define XD_D6_PD               (0x01 << 4)
+#define XD_D6_PU               (0x02 << 4)
+#define XD_D5_NP               0x00
+#define XD_D5_PD               (0x01 << 2)
+#define XD_D5_PU               (0x02 << 2)
+#define XD_D4_NP               0x00
+#define XD_D4_PD               0x01
+#define XD_D4_PU               0x02
+
+#define SD_D6_NP               0x00
+#define SD_D6_PD               (0x01 << 6)
+#define SD_D6_PU               (0x02 << 6)
+#define SD_D0_NP               0x00
+#define SD_D0_PD               (0x01 << 4)
+#define SD_D0_PU               (0x02 << 4)
+#define SD_D1_NP               0x00
+#define SD_D1_PD               0x01
+#define SD_D1_PU               0x02
+
+#define MS_D3_NP               0x00
+#define MS_D3_PD               (0x01 << 6)
+#define MS_D3_PU               (0x02 << 6)
+#define MS_D0_NP               0x00
+#define MS_D0_PD               (0x01 << 4)
+#define MS_D0_PU               (0x02 << 4)
+#define MS_BS_NP               0x00
+#define MS_BS_PD               (0x01 << 2)
+#define MS_BS_PU               (0x02 << 2)
+
+#define XD_WP_NP               0x00
+#define XD_WP_PD               (0x01 << 6)
+#define XD_WP_PU               (0x02 << 6)
+#define XD_CE_NP               0x00
+#define XD_CE_PD               (0x01 << 3)
+#define XD_CE_PU               (0x02 << 3)
+#define XD_CLE_NP              0x00
+#define XD_CLE_PD              (0x01 << 1)
+#define XD_CLE_PU              (0x02 << 1)
+#define XD_CD_PD               0x00
+#define XD_CD_PU               0x01
+
+#define SD_D4_NP               0x00
+#define SD_D4_PD               (0x01 << 6)
+#define SD_D4_PU               (0x02 << 6)
+
+#define MS_D7_NP               0x00
+#define MS_D7_PD               (0x01 << 6)
+#define MS_D7_PU               (0x02 << 6)
+
+#define XD_RDY_NP              0x00
+#define XD_RDY_PD              (0x01 << 6)
+#define XD_RDY_PU              (0x02 << 6)
+#define XD_WE_NP               0x00
+#define XD_WE_PD               (0x01 << 4)
+#define XD_WE_PU               (0x02 << 4)
+#define XD_RE_NP               0x00
+#define XD_RE_PD               (0x01 << 2)
+#define XD_RE_PU               (0x02 << 2)
+#define XD_ALE_NP              0x00
+#define XD_ALE_PD              0x01
+#define XD_ALE_PU              0x02
+
+#define SD_D3_NP               0x00
+#define SD_D3_PD               (0x01 << 4)
+#define SD_D3_PU               (0x02 << 4)
+#define SD_D2_NP               0x00
+#define SD_D2_PD               (0x01 << 2)
+#define SD_D2_PU               (0x02 << 2)
+
+#define MS_INS_PD              0x00
+#define MS_INS_PU              (0x01 << 7)
+#define SD_WP_NP               0x00
+#define SD_WP_PD               (0x01 << 5)
+#define SD_WP_PU               (0x02 << 5)
+#define SD_CD_PD               0x00
+#define SD_CD_PU               (0x01 << 4)
+#define SD_CMD_NP              0x00
+#define SD_CMD_PD              (0x01 << 2)
+#define SD_CMD_PU              (0x02 << 2)
+
+#define MS_D5_NP               0x00
+#define MS_D5_PD               (0x01 << 2)
+#define MS_D5_PU               (0x02 << 2)
+#define MS_D4_NP               0x00
+#define MS_D4_PD               0x01
+#define MS_D4_PU               0x02
+
+#define FORCE_PM_CLOCK         0x10
+#define EN_CLOCK_PM            0x01
+
+#define HOST_ENTER_S3          0x02
+#define HOST_ENTER_S1          0x01
+
+#define AUX_PWR_DETECTED       0x01
+
+#define PHY_DEBUG_MODE         0x01
+
+#define SPI_COMMAND_BIT_8      0xE0
+#define SPI_ADDRESS_BIT_24     0x17
+#define SPI_ADDRESS_BIT_32     0x1F
+
+#define SPI_TRANSFER0_START    0x80
+#define SPI_TRANSFER0_END      0x40
+#define SPI_C_MODE0            0x00
+#define SPI_CA_MODE0           0x01
+#define SPI_CDO_MODE0          0x02
+#define SPI_CDI_MODE0          0x03
+#define SPI_CADO_MODE0         0x04
+#define SPI_CADI_MODE0         0x05
+#define SPI_POLLING_MODE0      0x06
+
+#define SPI_TRANSFER1_START    0x80
+#define SPI_TRANSFER1_END      0x40
+#define SPI_DO_MODE1           0x00
+#define SPI_DI_MODE1           0x01
+
+#define CS_POLARITY_HIGH       0x40
+#define CS_POLARITY_LOW                0x00
+#define DTO_MSB_FIRST          0x00
+#define DTO_LSB_FIRST          0x20
+#define SPI_MASTER             0x00
+#define SPI_SLAVE              0x10
+#define SPI_MODE0              0x00
+#define SPI_MODE1              0x04
+#define SPI_MODE2              0x08
+#define SPI_MODE3              0x0C
+#define SPI_MANUAL             0x00
+#define SPI_HALF_AUTO          0x01
+#define SPI_AUTO               0x02
+#define SPI_EEPROM_AUTO                0x03
+
+#define EDO_TIMING_MASK                0x03
+#define SAMPLE_RISING          0x00
+#define SAMPLE_DELAY_HALF      0x01
+#define SAMPLE_DELAY_ONE       0x02
+#define SAPMLE_DELAY_ONE_HALF  0x03
+#define TCS_MASK               0x0C
+
+#define NOT_BYPASS_SD          0x02
+#define DISABLE_SDIO_FUNC      0x04
+#define SELECT_1LUN            0x08
+
+#define PWR_GATE_EN            0x01
+#define LDO3318_PWR_MASK       0x06
+#define LDO_ON                 0x00
+#define LDO_SUSPEND            0x04
+#define LDO_OFF                        0x06
+
+#define SD_CFG1                        0xFDA0
+#define SD_CFG2                        0xFDA1
+#define SD_CFG3                        0xFDA2
+#define SD_STAT1               0xFDA3
+#define SD_STAT2               0xFDA4
+#define SD_BUS_STAT            0xFDA5
+#define SD_PAD_CTL             0xFDA6
+#define SD_SAMPLE_POINT_CTL    0xFDA7
+#define SD_PUSH_POINT_CTL      0xFDA8
+#define SD_CMD0                        0xFDA9
+#define SD_CMD1                        0xFDAA
+#define SD_CMD2                        0xFDAB
+#define SD_CMD3                        0xFDAC
+#define SD_CMD4                        0xFDAD
+#define SD_CMD5                        0xFDAE
+#define SD_BYTE_CNT_L          0xFDAF
+#define SD_BYTE_CNT_H          0xFDB0
+#define SD_BLOCK_CNT_L         0xFDB1
+#define SD_BLOCK_CNT_H         0xFDB2
+#define SD_TRANSFER            0xFDB3
+#define SD_CMD_STATE           0xFDB5
+#define SD_DATA_STATE          0xFDB6
+
+#define        DCM_DRP_CTL             0xFC23
+#define        DCM_DRP_TRIG            0xFC24
+#define        DCM_DRP_CFG             0xFC25
+#define        DCM_DRP_WR_DATA_L       0xFC26
+#define        DCM_DRP_WR_DATA_H       0xFC27
+#define        DCM_DRP_RD_DATA_L       0xFC28
+#define        DCM_DRP_RD_DATA_H       0xFC29
+#define SD_VPCLK0_CTL          0xFC2A
+#define SD_VPCLK1_CTL          0xFC2B
+#define SD_DCMPS0_CTL          0xFC2C
+#define SD_DCMPS1_CTL          0xFC2D
+#define SD_VPTX_CTL            SD_VPCLK0_CTL
+#define SD_VPRX_CTL            SD_VPCLK1_CTL
+#define SD_DCMPS_TX_CTL                SD_DCMPS0_CTL
+#define SD_DCMPS_RX_CTL                SD_DCMPS1_CTL
+
+#define CARD_CLK_SOURCE                0xFC2E
+
+#define CARD_PWR_CTL           0xFD50
+#define CARD_CLK_SWITCH                0xFD51
+#define CARD_SHARE_MODE                0xFD52
+#define CARD_DRIVE_SEL         0xFD53
+#define CARD_STOP              0xFD54
+#define CARD_OE                        0xFD55
+#define CARD_AUTO_BLINK                0xFD56
+#define CARD_GPIO_DIR          0xFD57
+#define CARD_GPIO              0xFD58
+
+#define CARD_DATA_SOURCE       0xFD5B
+#define CARD_SELECT            0xFD5C
+#define SD30_DRIVE_SEL         0xFD5E
+
+#define CARD_CLK_EN            0xFD69
+
+#define SDIO_CTRL              0xFD6B
+
+#define FPDCTL                 0xFC00
+#define PDINFO                 0xFC01
+
+#define CLK_CTL                        0xFC02
+#define CLK_DIV                        0xFC03
+#define CLK_SEL                        0xFC04
+
+#define SSC_DIV_N_0            0xFC0F
+#define SSC_DIV_N_1            0xFC10
+
+#define RCCTL                  0xFC14
+
+#define FPGA_PULL_CTL          0xFC1D
+
+#define CARD_PULL_CTL1         0xFD60
+#define CARD_PULL_CTL2         0xFD61
+#define CARD_PULL_CTL3         0xFD62
+#define CARD_PULL_CTL4         0xFD63
+#define CARD_PULL_CTL5         0xFD64
+#define CARD_PULL_CTL6         0xFD65
+
+#define IRQEN0                         0xFE20
+#define IRQSTAT0                       0xFE21
+#define IRQEN1                         0xFE22
+#define IRQSTAT1                       0xFE23
+#define TLPRIEN                                0xFE24
+#define TLPRISTAT                      0xFE25
+#define TLPTIEN                                0xFE26
+#define TLPTISTAT                      0xFE27
+#define DMATC0                         0xFE28
+#define DMATC1                         0xFE29
+#define DMATC2                         0xFE2A
+#define DMATC3                         0xFE2B
+#define DMACTL                         0xFE2C
+#define BCTL                           0xFE2D
+#define RBBC0                          0xFE2E
+#define RBBC1                          0xFE2F
+#define RBDAT                          0xFE30
+#define RBCTL                          0xFE34
+#define CFGADDR0                       0xFE35
+#define CFGADDR1                       0xFE36
+#define CFGDATA0                       0xFE37
+#define CFGDATA1                       0xFE38
+#define CFGDATA2                       0xFE39
+#define CFGDATA3                       0xFE3A
+#define CFGRWCTL                       0xFE3B
+#define PHYRWCTL                       0xFE3C
+#define PHYDATA0                       0xFE3D
+#define PHYDATA1                       0xFE3E
+#define PHYADDR                                0xFE3F
+#define MSGRXDATA0                     0xFE40
+#define MSGRXDATA1                     0xFE41
+#define MSGRXDATA2                     0xFE42
+#define MSGRXDATA3                     0xFE43
+#define MSGTXDATA0                     0xFE44
+#define MSGTXDATA1                     0xFE45
+#define MSGTXDATA2                     0xFE46
+#define MSGTXDATA3                     0xFE47
+#define MSGTXCTL                       0xFE48
+#define PETXCFG                                0xFE49
+
+#define CDRESUMECTL                    0xFE52
+#define WAKE_SEL_CTL                   0xFE54
+#define PME_FORCE_CTL                  0xFE56
+#define ASPM_FORCE_CTL                 0xFE57
+#define PM_CLK_FORCE_CTL               0xFE58
+#define PERST_GLITCH_WIDTH             0xFE5C
+#define CHANGE_LINK_STATE              0xFE5B
+#define RESET_LOAD_REG                 0xFE5E
+#define HOST_SLEEP_STATE               0xFE60
+#define MAIN_PWR_OFF_CTL               0xFE70  /* RTS5208 */
+
+#define NFTS_TX_CTRL                   0xFE72
+
+#define PWR_GATE_CTRL                  0xFE75
+#define PWD_SUSPEND_EN                 0xFE76
+
+#define EFUSE_CONTENT                  0xFE5F
+
+#define XD_INIT                                0xFD10
+#define XD_DTCTL                       0xFD11
+#define XD_CTL                         0xFD12
+#define XD_TRANSFER                    0xFD13
+#define XD_CFG                         0xFD14
+#define XD_ADDRESS0                    0xFD15
+#define XD_ADDRESS1                    0xFD16
+#define XD_ADDRESS2                    0xFD17
+#define XD_ADDRESS3                    0xFD18
+#define XD_ADDRESS4                    0xFD19
+#define XD_DAT                         0xFD1A
+#define XD_PAGE_CNT                    0xFD1B
+#define XD_PAGE_STATUS                 0xFD1C
+#define XD_BLOCK_STATUS                        0xFD1D
+#define XD_BLOCK_ADDR1_L               0xFD1E
+#define XD_BLOCK_ADDR1_H               0xFD1F
+#define XD_BLOCK_ADDR2_L               0xFD20
+#define XD_BLOCK_ADDR2_H               0xFD21
+#define XD_BYTE_CNT_L                  0xFD22
+#define XD_BYTE_CNT_H                  0xFD23
+#define        XD_PARITY                       0xFD24
+#define XD_ECC_BIT1                    0xFD25
+#define XD_ECC_BYTE1                   0xFD26
+#define XD_ECC_BIT2                    0xFD27
+#define XD_ECC_BYTE2                   0xFD28
+#define XD_RESERVED0                   0xFD29
+#define XD_RESERVED1                   0xFD2A
+#define XD_RESERVED2                   0xFD2B
+#define XD_RESERVED3                   0xFD2C
+#define XD_CHK_DATA_STATUS             0xFD2D
+#define XD_CATCTL                      0xFD2E
+
+#define MS_CFG                         0xFD40
+#define MS_TPC                         0xFD41
+#define MS_TRANS_CFG                   0xFD42
+#define MS_TRANSFER                    0xFD43
+#define MS_INT_REG                     0xFD44
+#define MS_BYTE_CNT                    0xFD45
+#define MS_SECTOR_CNT_L                        0xFD46
+#define MS_SECTOR_CNT_H                        0xFD47
+#define MS_DBUS_H                      0xFD48
+
+#define SSC_CTL1                       0xFC11
+#define SSC_CTL2                       0xFC12
+
+#define OCPCTL                         0xFC15
+#define OCPSTAT                                0xFC16
+#define OCPCLR                         0xFC17  /* 5208 */
+#define OCPPARA1                       0xFC18
+#define OCPPARA2                       0xFC19
+
+#define EFUSE_OP                       0xFC20
+#define EFUSE_CTRL                     0xFC21
+#define EFUSE_DATA                     0xFC22
+
+#define        SPI_COMMAND                     0xFD80
+#define        SPI_ADDR0                       0xFD81
+#define        SPI_ADDR1                       0xFD82
+#define        SPI_ADDR2                       0xFD83
+#define        SPI_ADDR3                       0xFD84
+#define        SPI_CA_NUMBER                   0xFD85
+#define        SPI_LENGTH0                     0xFD86
+#define        SPI_LENGTH1                     0xFD87
+#define        SPI_DATA                        0xFD88
+#define SPI_DATA_NUMBER                        0xFD89
+#define        SPI_TRANSFER0                   0xFD90
+#define        SPI_TRANSFER1                   0xFD91
+#define        SPI_CONTROL                     0xFD92
+#define        SPI_SIG                         0xFD93
+#define        SPI_TCTL                        0xFD94
+#define        SPI_SLAVE_NUM                   0xFD95
+#define        SPI_CLK_DIVIDER0                0xFD96
+#define        SPI_CLK_DIVIDER1                0xFD97
+
+#define SRAM_BASE                      0xE600
+#define RBUF_BASE                      0xF400
+#define PPBUF_BASE1                    0xF800
+#define PPBUF_BASE2                    0xFA00
+#define IMAGE_FLAG_ADDR0               0xCE80
+#define IMAGE_FLAG_ADDR1               0xCE81
+
+#define READ_OP                        1
+#define WRITE_OP               2
+
+#define LCTLR          0x80
+
+#define POLLING_WAIT_CNT       1
+#define IDLE_MAX_COUNT         10
+#define SDIO_IDLE_COUNT                10
+
+#define DEBOUNCE_CNT                   5
+
+void do_remaining_work(struct rtsx_chip *chip);
+void try_to_switch_sdio_ctrl(struct rtsx_chip *chip);
+void do_reset_sd_card(struct rtsx_chip *chip);
+void do_reset_xd_card(struct rtsx_chip *chip);
+void do_reset_ms_card(struct rtsx_chip *chip);
+void rtsx_power_off_card(struct rtsx_chip *chip);
+void rtsx_release_cards(struct rtsx_chip *chip);
+void rtsx_reset_cards(struct rtsx_chip *chip);
+void rtsx_reinit_cards(struct rtsx_chip *chip, int reset_chip);
+void rtsx_init_cards(struct rtsx_chip *chip);
+int switch_ssc_clock(struct rtsx_chip *chip, int clk);
+int switch_normal_clock(struct rtsx_chip *chip, int clk);
+int enable_card_clock(struct rtsx_chip *chip, u8 card);
+int disable_card_clock(struct rtsx_chip *chip, u8 card);
+int card_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+       u32 sec_addr, u16 sec_cnt);
+void trans_dma_enable(enum dma_data_direction dir,
+               struct rtsx_chip *chip, u32 byte_cnt, u8 pack_size);
+void toggle_gpio(struct rtsx_chip *chip, u8 gpio);
+void turn_on_led(struct rtsx_chip *chip, u8 gpio);
+void turn_off_led(struct rtsx_chip *chip, u8 gpio);
+
+int card_share_mode(struct rtsx_chip *chip, int card);
+int select_card(struct rtsx_chip *chip, int card);
+int detect_card_cd(struct rtsx_chip *chip, int card);
+int check_card_exist(struct rtsx_chip *chip, unsigned int lun);
+int check_card_ready(struct rtsx_chip *chip, unsigned int lun);
+int check_card_wp(struct rtsx_chip *chip, unsigned int lun);
+int check_card_fail(struct rtsx_chip *chip, unsigned int lun);
+int check_card_ejected(struct rtsx_chip *chip, unsigned int lun);
+void eject_card(struct rtsx_chip *chip, unsigned int lun);
+u8 get_lun_card(struct rtsx_chip *chip, unsigned int lun);
+
+static inline u32 get_card_size(struct rtsx_chip *chip, unsigned int lun)
+{
+#ifdef SUPPORT_SD_LOCK
+       struct sd_info *sd_card = &(chip->sd_card);
+
+       if ((get_lun_card(chip, lun) == SD_CARD) &&
+               (sd_card->sd_lock_status & SD_LOCKED))
+               return 0;
+       else
+               return chip->capacity[lun];
+#else
+       return chip->capacity[lun];
+#endif
+}
+
+static inline int switch_clock(struct rtsx_chip *chip, int clk)
+{
+       int retval = 0;
+
+       if (chip->asic_code)
+               retval = switch_ssc_clock(chip, clk);
+       else
+               retval = switch_normal_clock(chip, clk);
+
+       return retval;
+}
+
+int card_power_on(struct rtsx_chip *chip, u8 card);
+int card_power_off(struct rtsx_chip *chip, u8 card);
+
+static inline int card_power_off_all(struct rtsx_chip *chip)
+{
+       RTSX_WRITE_REG(chip, CARD_PWR_CTL, 0x0F, 0x0F);
+
+       return STATUS_SUCCESS;
+}
+
+static inline void rtsx_clear_xd_error(struct rtsx_chip *chip)
+{
+       rtsx_write_register(chip, CARD_STOP, XD_STOP | XD_CLR_ERR,
+                       XD_STOP | XD_CLR_ERR);
+}
+
+static inline void rtsx_clear_sd_error(struct rtsx_chip *chip)
+{
+       rtsx_write_register(chip, CARD_STOP, SD_STOP | SD_CLR_ERR,
+                       SD_STOP | SD_CLR_ERR);
+}
+
+static inline void rtsx_clear_ms_error(struct rtsx_chip *chip)
+{
+       rtsx_write_register(chip, CARD_STOP, MS_STOP | MS_CLR_ERR,
+                       MS_STOP | MS_CLR_ERR);
+}
+
+static inline void rtsx_clear_spi_error(struct rtsx_chip *chip)
+{
+       rtsx_write_register(chip, CARD_STOP, SPI_STOP | SPI_CLR_ERR,
+                       SPI_STOP | SPI_CLR_ERR);
+}
+
+#ifdef SUPPORT_SDIO_ASPM
+void dynamic_configure_sdio_aspm(struct rtsx_chip *chip);
+#endif
+
+#endif  /* __REALTEK_RTSX_CARD_H */
diff --git a/drivers/staging/rts5208/rtsx_chip.c b/drivers/staging/rts5208/rtsx_chip.c
new file mode 100644 (file)
index 0000000..6426807
--- /dev/null
@@ -0,0 +1,1979 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/vmalloc.h>
+
+#include "rtsx.h"
+#include "rtsx_transport.h"
+#include "rtsx_scsi.h"
+#include "rtsx_card.h"
+#include "rtsx_chip.h"
+#include "rtsx_sys.h"
+#include "general.h"
+
+#include "sd.h"
+#include "xd.h"
+#include "ms.h"
+
+static void rtsx_calibration(struct rtsx_chip *chip)
+{
+       rtsx_write_phy_register(chip, 0x1B, 0x135E);
+       wait_timeout(10);
+       rtsx_write_phy_register(chip, 0x00, 0x0280);
+       rtsx_write_phy_register(chip, 0x01, 0x7112);
+       rtsx_write_phy_register(chip, 0x01, 0x7110);
+       rtsx_write_phy_register(chip, 0x01, 0x7112);
+       rtsx_write_phy_register(chip, 0x01, 0x7113);
+       rtsx_write_phy_register(chip, 0x00, 0x0288);
+}
+
+void rtsx_disable_card_int(struct rtsx_chip *chip)
+{
+       u32 reg = rtsx_readl(chip, RTSX_BIER);
+
+       reg &= ~(XD_INT_EN | SD_INT_EN | MS_INT_EN);
+       rtsx_writel(chip, RTSX_BIER, reg);
+}
+
+void rtsx_enable_card_int(struct rtsx_chip *chip)
+{
+       u32 reg = rtsx_readl(chip, RTSX_BIER);
+       int i;
+
+       for (i = 0; i <= chip->max_lun; i++) {
+               if (chip->lun2card[i] & XD_CARD)
+                       reg |= XD_INT_EN;
+               if (chip->lun2card[i] & SD_CARD)
+                       reg |= SD_INT_EN;
+               if (chip->lun2card[i] & MS_CARD)
+                       reg |= MS_INT_EN;
+       }
+       if (chip->hw_bypass_sd)
+               reg &= ~((u32)SD_INT_EN);
+
+       rtsx_writel(chip, RTSX_BIER, reg);
+}
+
+void rtsx_enable_bus_int(struct rtsx_chip *chip)
+{
+       u32 reg = 0;
+#ifndef DISABLE_CARD_INT
+       int i;
+#endif
+
+       reg = TRANS_OK_INT_EN | TRANS_FAIL_INT_EN;
+
+#ifndef DISABLE_CARD_INT
+       for (i = 0; i <= chip->max_lun; i++) {
+               RTSX_DEBUGP("lun2card[%d] = 0x%02x\n", i, chip->lun2card[i]);
+
+               if (chip->lun2card[i] & XD_CARD)
+                       reg |= XD_INT_EN;
+               if (chip->lun2card[i] & SD_CARD)
+                       reg |= SD_INT_EN;
+               if (chip->lun2card[i] & MS_CARD)
+                       reg |= MS_INT_EN;
+       }
+       if (chip->hw_bypass_sd)
+               reg &= ~((u32)SD_INT_EN);
+#endif
+
+       if (chip->ic_version >= IC_VER_C)
+               reg |= DELINK_INT_EN;
+#ifdef SUPPORT_OCP
+               reg |= OC_INT_EN;
+#endif
+       if (!chip->adma_mode)
+               reg |= DATA_DONE_INT_EN;
+
+       /* Enable Bus Interrupt */
+       rtsx_writel(chip, RTSX_BIER, reg);
+
+       RTSX_DEBUGP("RTSX_BIER: 0x%08x\n", reg);
+}
+
+void rtsx_disable_bus_int(struct rtsx_chip *chip)
+{
+       rtsx_writel(chip, RTSX_BIER, 0);
+}
+
+static int rtsx_pre_handle_sdio_old(struct rtsx_chip *chip)
+{
+       if (chip->ignore_sd && CHK_SDIO_EXIST(chip)) {
+               if (chip->asic_code) {
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL5, 0xFF,
+                               MS_INS_PU | SD_WP_PU | SD_CD_PU | SD_CMD_PU);
+               } else {
+                       RTSX_WRITE_REG(chip, FPGA_PULL_CTL, 0xFF,
+                               FPGA_SD_PULL_CTL_EN);
+               }
+               RTSX_WRITE_REG(chip, CARD_SHARE_MODE, 0xFF, CARD_SHARE_48_SD);
+
+               /* Enable SDIO internal clock */
+               RTSX_WRITE_REG(chip, 0xFF2C, 0x01, 0x01);
+
+               RTSX_WRITE_REG(chip, SDIO_CTRL, 0xFF,
+                       SDIO_BUS_CTRL | SDIO_CD_CTRL);
+
+               chip->sd_int = 1;
+               chip->sd_io = 1;
+       } else {
+               chip->need_reset |= SD_CARD;
+       }
+
+       return STATUS_SUCCESS;
+}
+
+#ifdef HW_AUTO_SWITCH_SD_BUS
+static int rtsx_pre_handle_sdio_new(struct rtsx_chip *chip)
+{
+       u8 tmp;
+       int sw_bypass_sd = 0;
+       int retval;
+
+       if (chip->driver_first_load) {
+               if (CHECK_PID(chip, 0x5288)) {
+                       RTSX_READ_REG(chip, 0xFE5A, &tmp);
+                       if (tmp & 0x08)
+                               sw_bypass_sd = 1;
+               } else if (CHECK_PID(chip, 0x5208)) {
+                       RTSX_READ_REG(chip, 0xFE70, &tmp);
+                       if (tmp & 0x80)
+                               sw_bypass_sd = 1;
+               }
+       } else {
+               if (chip->sdio_in_charge)
+                       sw_bypass_sd = 1;
+       }
+       RTSX_DEBUGP("chip->sdio_in_charge = %d\n", chip->sdio_in_charge);
+       RTSX_DEBUGP("chip->driver_first_load = %d\n", chip->driver_first_load);
+       RTSX_DEBUGP("sw_bypass_sd = %d\n", sw_bypass_sd);
+
+       if (sw_bypass_sd) {
+               u8 cd_toggle_mask = 0;
+
+               RTSX_READ_REG(chip, TLPTISTAT, &tmp);
+               cd_toggle_mask = 0x08;
+
+               if (tmp & cd_toggle_mask) {
+                       /* Disable sdio_bus_auto_switch */
+                       if (CHECK_PID(chip, 0x5288))
+                               RTSX_WRITE_REG(chip, 0xFE5A, 0x08, 0x00);
+                       else if (CHECK_PID(chip, 0x5208))
+                               RTSX_WRITE_REG(chip, 0xFE70, 0x80, 0x00);
+
+                       RTSX_WRITE_REG(chip, TLPTISTAT, 0xFF, tmp);
+
+                       chip->need_reset |= SD_CARD;
+               } else {
+                       RTSX_DEBUGP("Chip inserted with SDIO!\n");
+
+                       if (chip->asic_code) {
+                               retval = sd_pull_ctl_enable(chip);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+                       } else {
+                               RTSX_WRITE_REG(chip, FPGA_PULL_CTL,
+                                       FPGA_SD_PULL_CTL_BIT | 0x20, 0);
+                       }
+                       retval = card_share_mode(chip, SD_CARD);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       /* Enable sdio_bus_auto_switch */
+                       if (CHECK_PID(chip, 0x5288))
+                               RTSX_WRITE_REG(chip, 0xFE5A, 0x08, 0x08);
+                       else if (CHECK_PID(chip, 0x5208))
+                               RTSX_WRITE_REG(chip, 0xFE70, 0x80, 0x80);
+
+                       chip->chip_insert_with_sdio = 1;
+                       chip->sd_io = 1;
+               }
+       } else {
+               RTSX_WRITE_REG(chip, TLPTISTAT, 0x08, 0x08);
+
+               chip->need_reset |= SD_CARD;
+       }
+
+       return STATUS_SUCCESS;
+}
+#endif
+
+int rtsx_reset_chip(struct rtsx_chip *chip)
+{
+       int retval;
+
+       rtsx_writel(chip, RTSX_HCBAR, chip->host_cmds_addr);
+
+       rtsx_disable_aspm(chip);
+
+       RTSX_WRITE_REG(chip, HOST_SLEEP_STATE, 0x03, 0x00);
+
+       /* Disable card clock */
+       RTSX_WRITE_REG(chip, CARD_CLK_EN, 0x1E, 0);
+
+#ifdef SUPPORT_OCP
+       /* SSC power on, OCD power on */
+       if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
+               RTSX_WRITE_REG(chip, FPDCTL, OC_POWER_DOWN, 0);
+       else
+               RTSX_WRITE_REG(chip, FPDCTL, OC_POWER_DOWN, MS_OC_POWER_DOWN);
+
+       RTSX_WRITE_REG(chip, OCPPARA1, OCP_TIME_MASK, OCP_TIME_800);
+       RTSX_WRITE_REG(chip, OCPPARA2, OCP_THD_MASK, OCP_THD_244_946);
+       RTSX_WRITE_REG(chip, OCPCTL, 0xFF, CARD_OC_INT_EN | CARD_DETECT_EN);
+#else
+       /* OC power down */
+       RTSX_WRITE_REG(chip, FPDCTL, OC_POWER_DOWN, OC_POWER_DOWN);
+#endif
+
+       if (!CHECK_PID(chip, 0x5288))
+               RTSX_WRITE_REG(chip, CARD_GPIO_DIR, 0xFF, 0x03);
+
+       /* Turn off LED */
+       RTSX_WRITE_REG(chip, CARD_GPIO, 0xFF, 0x03);
+
+       /* Reset delink mode */
+       RTSX_WRITE_REG(chip, CHANGE_LINK_STATE, 0x0A, 0);
+
+       /* Card driving select */
+       RTSX_WRITE_REG(chip, CARD_DRIVE_SEL, 0xFF, chip->card_drive_sel);
+
+#ifdef LED_AUTO_BLINK
+       RTSX_WRITE_REG(chip, CARD_AUTO_BLINK, 0xFF,
+                       LED_BLINK_SPEED | BLINK_EN | LED_GPIO0);
+#endif
+
+       if (chip->asic_code) {
+               /* Enable SSC Clock */
+               RTSX_WRITE_REG(chip, SSC_CTL1, 0xFF, SSC_8X_EN | SSC_SEL_4M);
+               RTSX_WRITE_REG(chip, SSC_CTL2, 0xFF, 0x12);
+       }
+
+       /* Disable cd_pwr_save (u_force_rst_core_en=0, u_cd_rst_core_en=0)
+             0xFE5B
+             bit[1]    u_cd_rst_core_en        rst_value = 0
+             bit[2]    u_force_rst_core_en     rst_value = 0
+             bit[5]    u_mac_phy_rst_n_dbg     rst_value = 1
+             bit[4]    u_non_sticky_rst_n_dbg  rst_value = 0
+       */
+       RTSX_WRITE_REG(chip, CHANGE_LINK_STATE, 0x16, 0x10);
+
+       /* Enable ASPM */
+       if (chip->aspm_l0s_l1_en) {
+               if (chip->dynamic_aspm) {
+                       if (CHK_SDIO_EXIST(chip)) {
+                               if (CHECK_PID(chip, 0x5288)) {
+                                       retval = rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFF, chip->aspm_l0s_l1_en);
+                                       if (retval != STATUS_SUCCESS)
+                                               TRACE_RET(chip, STATUS_FAIL);
+                               }
+                       }
+               } else {
+                       if (CHECK_PID(chip, 0x5208))
+                               RTSX_WRITE_REG(chip, ASPM_FORCE_CTL,
+                                       0xFF, 0x3F);
+
+                       retval = rtsx_write_config_byte(chip, LCTLR,
+                                                       chip->aspm_l0s_l1_en);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       chip->aspm_level[0] = chip->aspm_l0s_l1_en;
+                       if (CHK_SDIO_EXIST(chip)) {
+                               chip->aspm_level[1] = chip->aspm_l0s_l1_en;
+                               if (CHECK_PID(chip, 0x5288))
+                                       retval = rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFF, chip->aspm_l0s_l1_en);
+                               else
+                                       retval = rtsx_write_cfg_dw(chip, 1, 0xC0, 0xFF, chip->aspm_l0s_l1_en);
+
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+
+                       }
+
+                       chip->aspm_enabled = 1;
+               }
+       } else {
+               if (chip->asic_code && CHECK_PID(chip, 0x5208)) {
+                       retval = rtsx_write_phy_register(chip, 0x07, 0x0129);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+               }
+               retval = rtsx_write_config_byte(chip, LCTLR,
+                                               chip->aspm_l0s_l1_en);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = rtsx_write_config_byte(chip, 0x81, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (CHK_SDIO_EXIST(chip)) {
+               if (CHECK_PID(chip, 0x5288))
+                       retval = rtsx_write_cfg_dw(chip, 2, 0xC0,
+                                               0xFF00, 0x0100);
+               else
+                       retval = rtsx_write_cfg_dw(chip, 1, 0xC0,
+                                               0xFF00, 0x0100);
+
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+       }
+
+       if (CHECK_PID(chip, 0x5288)) {
+               if (!CHK_SDIO_EXIST(chip)) {
+                       retval = rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFFFF,
+                                               0x0103);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       retval = rtsx_write_cfg_dw(chip, 2, 0x84, 0xFF, 0x03);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+               }
+       }
+
+       RTSX_WRITE_REG(chip, IRQSTAT0, LINK_RDY_INT, LINK_RDY_INT);
+
+       RTSX_WRITE_REG(chip, PERST_GLITCH_WIDTH, 0xFF, 0x80);
+
+       /* Enable PCIE interrupt */
+       if (chip->asic_code) {
+               if (CHECK_PID(chip, 0x5208)) {
+                       if (chip->phy_debug_mode) {
+                               RTSX_WRITE_REG(chip, CDRESUMECTL, 0x77, 0);
+                               rtsx_disable_bus_int(chip);
+                       } else {
+                               rtsx_enable_bus_int(chip);
+                       }
+
+                       if (chip->ic_version >= IC_VER_D) {
+                               u16 reg;
+                               retval = rtsx_read_phy_register(chip, 0x00,
+                                                               &reg);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+
+                               reg &= 0xFE7F;
+                               reg |= 0x80;
+                               retval = rtsx_write_phy_register(chip, 0x00,
+                                                               reg);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+
+                               retval = rtsx_read_phy_register(chip, 0x1C,
+                                                               &reg);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+
+                               reg &= 0xFFF7;
+                               retval = rtsx_write_phy_register(chip, 0x1C,
+                                                               reg);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+
+                       }
+
+                       if (chip->driver_first_load &&
+                               (chip->ic_version < IC_VER_C))
+                               rtsx_calibration(chip);
+
+               } else {
+                       rtsx_enable_bus_int(chip);
+               }
+       } else {
+               rtsx_enable_bus_int(chip);
+       }
+
+       chip->need_reset = 0;
+
+       chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
+
+       if (chip->hw_bypass_sd)
+               goto NextCard;
+       RTSX_DEBUGP("In rtsx_reset_chip, chip->int_reg = 0x%x\n",
+               chip->int_reg);
+       if (chip->int_reg & SD_EXIST) {
+#ifdef HW_AUTO_SWITCH_SD_BUS
+               if (CHECK_PID(chip, 0x5208) && (chip->ic_version < IC_VER_C))
+                       retval = rtsx_pre_handle_sdio_old(chip);
+               else
+                       retval = rtsx_pre_handle_sdio_new(chip);
+
+               RTSX_DEBUGP("chip->need_reset = 0x%x (rtsx_reset_chip)\n",
+                       (unsigned int)(chip->need_reset));
+#else  /* HW_AUTO_SWITCH_SD_BUS */
+               retval = rtsx_pre_handle_sdio_old(chip);
+#endif  /* HW_AUTO_SWITCH_SD_BUS */
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+       } else {
+               chip->sd_io = 0;
+               RTSX_WRITE_REG(chip, SDIO_CTRL, SDIO_BUS_CTRL | SDIO_CD_CTRL,
+                       0);
+       }
+
+NextCard:
+       if (chip->int_reg & XD_EXIST)
+               chip->need_reset |= XD_CARD;
+       if (chip->int_reg & MS_EXIST)
+               chip->need_reset |= MS_CARD;
+       if (chip->int_reg & CARD_EXIST)
+               RTSX_WRITE_REG(chip, SSC_CTL1, SSC_RSTB, SSC_RSTB);
+
+       RTSX_DEBUGP("In rtsx_init_chip, chip->need_reset = 0x%x\n",
+               (unsigned int)(chip->need_reset));
+
+       RTSX_WRITE_REG(chip, RCCTL, 0x01, 0x00);
+
+       if (CHECK_PID(chip, 0x5208) || CHECK_PID(chip, 0x5288)) {
+               /* Turn off main power when entering S3/S4 state */
+               RTSX_WRITE_REG(chip, MAIN_PWR_OFF_CTL, 0x03, 0x03);
+       }
+
+       if (chip->remote_wakeup_en && !chip->auto_delink_en) {
+               RTSX_WRITE_REG(chip, WAKE_SEL_CTL, 0x07, 0x07);
+               if (chip->aux_pwr_exist)
+                       RTSX_WRITE_REG(chip, PME_FORCE_CTL, 0xFF, 0x33);
+       } else {
+               RTSX_WRITE_REG(chip, WAKE_SEL_CTL, 0x07, 0x04);
+               RTSX_WRITE_REG(chip, PME_FORCE_CTL, 0xFF, 0x30);
+       }
+
+       if (CHECK_PID(chip, 0x5208) && (chip->ic_version >= IC_VER_D))
+               RTSX_WRITE_REG(chip, PETXCFG, 0x1C, 0x14);
+
+       if (chip->asic_code && CHECK_PID(chip, 0x5208)) {
+               retval = rtsx_clr_phy_reg_bit(chip, 0x1C, 2);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (chip->ft2_fast_mode) {
+               RTSX_WRITE_REG(chip, CARD_PWR_CTL, 0xFF,
+                       MS_PARTIAL_POWER_ON | SD_PARTIAL_POWER_ON);
+               udelay(chip->pmos_pwr_on_interval);
+               RTSX_WRITE_REG(chip, CARD_PWR_CTL, 0xFF,
+                       MS_POWER_ON | SD_POWER_ON);
+
+               wait_timeout(200);
+       }
+
+       /* Reset card */
+       rtsx_reset_detected_cards(chip, 0);
+
+       chip->driver_first_load = 0;
+
+       return STATUS_SUCCESS;
+}
+
+static inline int check_sd_speed_prior(u32 sd_speed_prior)
+{
+       int i, fake_para = 0;
+
+       for (i = 0; i < 4; i++) {
+               u8 tmp = (u8)(sd_speed_prior >> (i*8));
+               if ((tmp < 0x01) || (tmp > 0x04)) {
+                       fake_para = 1;
+                       break;
+               }
+       }
+
+       return !fake_para;
+}
+
+static inline int check_sd_current_prior(u32 sd_current_prior)
+{
+       int i, fake_para = 0;
+
+       for (i = 0; i < 4; i++) {
+               u8 tmp = (u8)(sd_current_prior >> (i*8));
+               if (tmp > 0x03) {
+                       fake_para = 1;
+                       break;
+               }
+       }
+
+       return !fake_para;
+}
+
+static int rts5208_init(struct rtsx_chip *chip)
+{
+       int retval;
+       u16 reg = 0;
+       u8 val = 0;
+
+       RTSX_WRITE_REG(chip, CLK_SEL, 0x03, 0x03);
+       RTSX_READ_REG(chip, CLK_SEL, &val);
+       if (val == 0)
+               chip->asic_code = 1;
+       else
+               chip->asic_code = 0;
+
+       if (chip->asic_code) {
+               retval = rtsx_read_phy_register(chip, 0x1C, &reg);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               RTSX_DEBUGP("Value of phy register 0x1C is 0x%x\n", reg);
+               chip->ic_version = (reg >> 4) & 0x07;
+               if (reg & PHY_DEBUG_MODE)
+                       chip->phy_debug_mode = 1;
+               else
+                       chip->phy_debug_mode = 0;
+
+       } else {
+               RTSX_READ_REG(chip, 0xFE80, &val);
+               chip->ic_version = val;
+               chip->phy_debug_mode = 0;
+       }
+
+       RTSX_READ_REG(chip, PDINFO, &val);
+       RTSX_DEBUGP("PDINFO: 0x%x\n", val);
+       if (val & AUX_PWR_DETECTED)
+               chip->aux_pwr_exist = 1;
+       else
+               chip->aux_pwr_exist = 0;
+
+       RTSX_READ_REG(chip, 0xFE50, &val);
+       if (val & 0x01)
+               chip->hw_bypass_sd = 1;
+       else
+               chip->hw_bypass_sd = 0;
+
+       rtsx_read_config_byte(chip, 0x0E, &val);
+       if (val & 0x80)
+               SET_SDIO_EXIST(chip);
+       else
+               CLR_SDIO_EXIST(chip);
+
+       if (chip->use_hw_setting) {
+               RTSX_READ_REG(chip, CHANGE_LINK_STATE, &val);
+               if (val & 0x80)
+                       chip->auto_delink_en = 1;
+               else
+                       chip->auto_delink_en = 0;
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int rts5288_init(struct rtsx_chip *chip)
+{
+       int retval;
+       u8 val = 0, max_func;
+       u32 lval = 0;
+
+       RTSX_WRITE_REG(chip, CLK_SEL, 0x03, 0x03);
+       RTSX_READ_REG(chip, CLK_SEL, &val);
+       if (val == 0)
+               chip->asic_code = 1;
+       else
+               chip->asic_code = 0;
+
+       chip->ic_version = 0;
+       chip->phy_debug_mode = 0;
+
+       RTSX_READ_REG(chip, PDINFO, &val);
+       RTSX_DEBUGP("PDINFO: 0x%x\n", val);
+       if (val & AUX_PWR_DETECTED)
+               chip->aux_pwr_exist = 1;
+       else
+               chip->aux_pwr_exist = 0;
+
+       RTSX_READ_REG(chip, CARD_SHARE_MODE, &val);
+       RTSX_DEBUGP("CARD_SHARE_MODE: 0x%x\n", val);
+       if (val & 0x04)
+               chip->baro_pkg = QFN;
+       else
+               chip->baro_pkg = LQFP;
+
+       RTSX_READ_REG(chip, 0xFE5A, &val);
+       if (val & 0x10)
+               chip->hw_bypass_sd = 1;
+       else
+               chip->hw_bypass_sd = 0;
+
+       retval = rtsx_read_cfg_dw(chip, 0, 0x718, &lval);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       max_func = (u8)((lval >> 29) & 0x07);
+       RTSX_DEBUGP("Max function number: %d\n", max_func);
+       if (max_func == 0x02)
+               SET_SDIO_EXIST(chip);
+       else
+               CLR_SDIO_EXIST(chip);
+
+       if (chip->use_hw_setting) {
+               RTSX_READ_REG(chip, CHANGE_LINK_STATE, &val);
+               if (val & 0x80)
+                       chip->auto_delink_en = 1;
+               else
+                       chip->auto_delink_en = 0;
+
+               if (CHECK_BARO_PKG(chip, LQFP))
+                       chip->lun_mode = SD_MS_1LUN;
+               else
+                       chip->lun_mode = DEFAULT_SINGLE;
+
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_init_chip(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       struct xd_info *xd_card = &(chip->xd_card);
+       struct ms_info *ms_card = &(chip->ms_card);
+       int retval;
+       unsigned int i;
+
+       RTSX_DEBUGP("Vendor ID: 0x%04x, Product ID: 0x%04x\n",
+                    chip->vendor_id, chip->product_id);
+
+       chip->ic_version = 0;
+
+#ifdef _MSG_TRACE
+       chip->msg_idx = 0;
+#endif
+
+       memset(xd_card, 0, sizeof(struct xd_info));
+       memset(sd_card, 0, sizeof(struct sd_info));
+       memset(ms_card, 0, sizeof(struct ms_info));
+
+       chip->xd_reset_counter = 0;
+       chip->sd_reset_counter = 0;
+       chip->ms_reset_counter = 0;
+
+       chip->xd_show_cnt = MAX_SHOW_CNT;
+       chip->sd_show_cnt = MAX_SHOW_CNT;
+       chip->ms_show_cnt = MAX_SHOW_CNT;
+
+       chip->sd_io = 0;
+       chip->auto_delink_cnt = 0;
+       chip->auto_delink_allowed = 1;
+       rtsx_set_stat(chip, RTSX_STAT_INIT);
+
+       chip->aspm_enabled = 0;
+       chip->chip_insert_with_sdio = 0;
+       chip->sdio_aspm = 0;
+       chip->sdio_idle = 0;
+       chip->sdio_counter = 0;
+       chip->cur_card = 0;
+       chip->phy_debug_mode = 0;
+       chip->sdio_func_exist = 0;
+       memset(chip->sdio_raw_data, 0, 12);
+
+       for (i = 0; i < MAX_ALLOWED_LUN_CNT; i++) {
+               set_sense_type(chip, i, SENSE_TYPE_NO_SENSE);
+               chip->rw_fail_cnt[i] = 0;
+       }
+
+       if (!check_sd_speed_prior(chip->sd_speed_prior))
+               chip->sd_speed_prior = 0x01040203;
+
+       RTSX_DEBUGP("sd_speed_prior = 0x%08x\n", chip->sd_speed_prior);
+
+       if (!check_sd_current_prior(chip->sd_current_prior))
+               chip->sd_current_prior = 0x00010203;
+
+       RTSX_DEBUGP("sd_current_prior = 0x%08x\n", chip->sd_current_prior);
+
+       if ((chip->sd_ddr_tx_phase > 31) || (chip->sd_ddr_tx_phase < 0))
+               chip->sd_ddr_tx_phase = 0;
+
+       if ((chip->mmc_ddr_tx_phase > 31) || (chip->mmc_ddr_tx_phase < 0))
+               chip->mmc_ddr_tx_phase = 0;
+
+       RTSX_WRITE_REG(chip, FPDCTL, SSC_POWER_DOWN, 0);
+       wait_timeout(200);
+       RTSX_WRITE_REG(chip, CLK_DIV, 0x07, 0x07);
+       RTSX_DEBUGP("chip->use_hw_setting = %d\n", chip->use_hw_setting);
+
+       if (CHECK_PID(chip, 0x5208)) {
+               retval = rts5208_init(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+       } else if (CHECK_PID(chip, 0x5288)) {
+               retval = rts5288_init(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+       }
+
+       if (chip->ss_en == 2)
+               chip->ss_en = 0;
+
+       RTSX_DEBUGP("chip->asic_code = %d\n", chip->asic_code);
+       RTSX_DEBUGP("chip->ic_version = 0x%x\n", chip->ic_version);
+       RTSX_DEBUGP("chip->phy_debug_mode = %d\n", chip->phy_debug_mode);
+       RTSX_DEBUGP("chip->aux_pwr_exist = %d\n", chip->aux_pwr_exist);
+       RTSX_DEBUGP("chip->sdio_func_exist = %d\n", chip->sdio_func_exist);
+       RTSX_DEBUGP("chip->hw_bypass_sd = %d\n", chip->hw_bypass_sd);
+       RTSX_DEBUGP("chip->aspm_l0s_l1_en = %d\n", chip->aspm_l0s_l1_en);
+       RTSX_DEBUGP("chip->lun_mode = %d\n", chip->lun_mode);
+       RTSX_DEBUGP("chip->auto_delink_en = %d\n", chip->auto_delink_en);
+       RTSX_DEBUGP("chip->ss_en = %d\n", chip->ss_en);
+       RTSX_DEBUGP("chip->baro_pkg = %d\n", chip->baro_pkg);
+
+       if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+               chip->card2lun[SD_CARD] = 0;
+               chip->card2lun[MS_CARD] = 1;
+               chip->card2lun[XD_CARD] = 0xFF;
+               chip->lun2card[0] = SD_CARD;
+               chip->lun2card[1] = MS_CARD;
+               chip->max_lun = 1;
+               SET_SDIO_IGNORED(chip);
+       } else if (CHECK_LUN_MODE(chip, SD_MS_1LUN)) {
+               chip->card2lun[SD_CARD] = 0;
+               chip->card2lun[MS_CARD] = 0;
+               chip->card2lun[XD_CARD] = 0xFF;
+               chip->lun2card[0] = SD_CARD | MS_CARD;
+               chip->max_lun = 0;
+       } else {
+               chip->card2lun[XD_CARD] = 0;
+               chip->card2lun[SD_CARD] = 0;
+               chip->card2lun[MS_CARD] = 0;
+               chip->lun2card[0] = XD_CARD | SD_CARD | MS_CARD;
+               chip->max_lun = 0;
+       }
+
+       retval = rtsx_reset_chip(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+void rtsx_release_chip(struct rtsx_chip *chip)
+{
+       xd_free_l2p_tbl(chip);
+       ms_free_l2p_tbl(chip);
+       chip->card_exist = 0;
+       chip->card_ready = 0;
+}
+
+#if !defined(LED_AUTO_BLINK) && defined(REGULAR_BLINK)
+static inline void rtsx_blink_led(struct rtsx_chip *chip)
+{
+       if (chip->card_exist && chip->blink_led) {
+               if (chip->led_toggle_counter < LED_TOGGLE_INTERVAL) {
+                       chip->led_toggle_counter++;
+               } else {
+                       chip->led_toggle_counter = 0;
+                       toggle_gpio(chip, LED_GPIO);
+               }
+       }
+}
+#endif
+
+static void rtsx_monitor_aspm_config(struct rtsx_chip *chip)
+{
+       int maybe_support_aspm, reg_changed;
+       u32 tmp = 0;
+       u8 reg0 = 0, reg1 = 0;
+
+       maybe_support_aspm = 0;
+       reg_changed = 0;
+       rtsx_read_config_byte(chip, LCTLR, &reg0);
+       if (chip->aspm_level[0] != reg0) {
+               reg_changed = 1;
+               chip->aspm_level[0] = reg0;
+       }
+       if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) {
+               rtsx_read_cfg_dw(chip, 1, 0xC0, &tmp);
+               reg1 = (u8)tmp;
+               if (chip->aspm_level[1] != reg1) {
+                       reg_changed = 1;
+                       chip->aspm_level[1] = reg1;
+               }
+
+               if ((reg0 & 0x03) && (reg1 & 0x03))
+                       maybe_support_aspm = 1;
+
+       } else {
+               if (reg0 & 0x03)
+                       maybe_support_aspm = 1;
+
+       }
+
+       if (reg_changed) {
+               if (maybe_support_aspm)
+                       chip->aspm_l0s_l1_en = 0x03;
+
+               RTSX_DEBUGP("aspm_level[0] = 0x%02x, aspm_level[1] = 0x%02x\n",
+                             chip->aspm_level[0], chip->aspm_level[1]);
+
+               if (chip->aspm_l0s_l1_en) {
+                       chip->aspm_enabled = 1;
+               } else {
+                       chip->aspm_enabled = 0;
+                       chip->sdio_aspm = 0;
+               }
+               rtsx_write_register(chip, ASPM_FORCE_CTL, 0xFF,
+                               0x30 | chip->aspm_level[0] |
+                               (chip->aspm_level[1] << 2));
+       }
+}
+
+void rtsx_polling_func(struct rtsx_chip *chip)
+{
+#ifdef SUPPORT_SD_LOCK
+       struct sd_info *sd_card = &(chip->sd_card);
+#endif
+       int ss_allowed;
+
+       if (rtsx_chk_stat(chip, RTSX_STAT_SUSPEND))
+               return;
+
+       if (rtsx_chk_stat(chip, RTSX_STAT_DELINK))
+               goto Delink_Stage;
+
+       if (chip->polling_config) {
+               u8 val;
+               rtsx_read_config_byte(chip, 0, &val);
+       }
+
+       if (rtsx_chk_stat(chip, RTSX_STAT_SS))
+               return;
+
+#ifdef SUPPORT_OCP
+       if (chip->ocp_int) {
+               rtsx_read_register(chip, OCPSTAT, &(chip->ocp_stat));
+
+               if (chip->card_exist & SD_CARD)
+                       sd_power_off_card3v3(chip);
+               else if (chip->card_exist & MS_CARD)
+                       ms_power_off_card3v3(chip);
+               else if (chip->card_exist & XD_CARD)
+                       xd_power_off_card3v3(chip);
+
+               chip->ocp_int = 0;
+       }
+#endif
+
+#ifdef SUPPORT_SD_LOCK
+       if (sd_card->sd_erase_status) {
+               if (chip->card_exist & SD_CARD) {
+                       u8 val;
+                       rtsx_read_register(chip, 0xFD30, &val);
+                       if (val & 0x02) {
+                               sd_card->sd_erase_status = SD_NOT_ERASE;
+                               sd_card->sd_lock_notify = 1;
+                               chip->need_reinit |= SD_CARD;
+                       }
+               } else {
+                       sd_card->sd_erase_status = SD_NOT_ERASE;
+               }
+       }
+#endif
+
+       rtsx_init_cards(chip);
+
+       if (chip->ss_en) {
+               ss_allowed = 1;
+
+               if (CHECK_PID(chip, 0x5288)) {
+                       ss_allowed = 0;
+               } else {
+                       if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) {
+                               u32 val;
+                               rtsx_read_cfg_dw(chip, 1, 0x04, &val);
+                               if (val & 0x07)
+                                       ss_allowed = 0;
+
+                       }
+               }
+       } else {
+               ss_allowed = 0;
+       }
+
+       if (ss_allowed && !chip->sd_io) {
+               if (rtsx_get_stat(chip) != RTSX_STAT_IDLE) {
+                       chip->ss_counter = 0;
+               } else {
+                       if (chip->ss_counter <
+                               (chip->ss_idle_period / POLLING_INTERVAL)) {
+                               chip->ss_counter++;
+                       } else {
+                               rtsx_exclusive_enter_ss(chip);
+                               return;
+                       }
+               }
+       }
+
+       if (CHECK_PID(chip, 0x5208)) {
+               rtsx_monitor_aspm_config(chip);
+
+#ifdef SUPPORT_SDIO_ASPM
+               if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip) &&
+                               chip->aspm_l0s_l1_en && chip->dynamic_aspm) {
+                       if (chip->sd_io) {
+                               dynamic_configure_sdio_aspm(chip);
+                       } else {
+                               if (!chip->sdio_aspm) {
+                                       RTSX_DEBUGP("SDIO enter ASPM!\n");
+                                       rtsx_write_register(chip,
+                                               ASPM_FORCE_CTL, 0xFC,
+                                               0x30 | (chip->aspm_level[1] << 2));
+                                       chip->sdio_aspm = 1;
+                               }
+                       }
+               }
+#endif
+       }
+
+       if (chip->idle_counter < IDLE_MAX_COUNT) {
+               chip->idle_counter++;
+       } else {
+               if (rtsx_get_stat(chip) != RTSX_STAT_IDLE) {
+                       RTSX_DEBUGP("Idle state!\n");
+                       rtsx_set_stat(chip, RTSX_STAT_IDLE);
+
+#if !defined(LED_AUTO_BLINK) && defined(REGULAR_BLINK)
+                       chip->led_toggle_counter = 0;
+#endif
+                       rtsx_force_power_on(chip, SSC_PDCTL);
+
+                       turn_off_led(chip, LED_GPIO);
+
+                       if (chip->auto_power_down && !chip->card_ready && !chip->sd_io)
+                               rtsx_force_power_down(chip, SSC_PDCTL | OC_PDCTL);
+
+               }
+       }
+
+       switch (rtsx_get_stat(chip)) {
+       case RTSX_STAT_RUN:
+#if !defined(LED_AUTO_BLINK) && defined(REGULAR_BLINK)
+               rtsx_blink_led(chip);
+#endif
+               do_remaining_work(chip);
+               break;
+
+       case RTSX_STAT_IDLE:
+               if (chip->sd_io && !chip->sd_int)
+                       try_to_switch_sdio_ctrl(chip);
+
+               rtsx_enable_aspm(chip);
+               break;
+
+       default:
+               break;
+       }
+
+
+#ifdef SUPPORT_OCP
+       if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+#ifdef CONFIG_RTS5208_DEBUG
+               if (chip->ocp_stat &
+                       (SD_OC_NOW | SD_OC_EVER | MS_OC_NOW | MS_OC_EVER))
+                       RTSX_DEBUGP("Over current, OCPSTAT is 0x%x\n",
+                               chip->ocp_stat);
+#endif
+
+               if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
+                       if (chip->card_exist & SD_CARD) {
+                               rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN,
+                                               0);
+                               card_power_off(chip, SD_CARD);
+                               chip->card_fail |= SD_CARD;
+                       }
+               }
+               if (chip->ocp_stat & (MS_OC_NOW | MS_OC_EVER)) {
+                       if (chip->card_exist & MS_CARD) {
+                               rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN,
+                                               0);
+                               card_power_off(chip, MS_CARD);
+                               chip->card_fail |= MS_CARD;
+                       }
+               }
+       } else {
+               if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
+                       RTSX_DEBUGP("Over current, OCPSTAT is 0x%x\n",
+                               chip->ocp_stat);
+                       if (chip->card_exist & SD_CARD) {
+                               rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN,
+                                               0);
+                               chip->card_fail |= SD_CARD;
+                       } else if (chip->card_exist & MS_CARD) {
+                               rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN,
+                                               0);
+                               chip->card_fail |= MS_CARD;
+                       } else if (chip->card_exist & XD_CARD) {
+                               rtsx_write_register(chip, CARD_OE, XD_OUTPUT_EN,
+                                               0);
+                               chip->card_fail |= XD_CARD;
+                       }
+                       card_power_off(chip, SD_CARD);
+               }
+       }
+#endif
+
+Delink_Stage:
+       if (chip->auto_delink_en && chip->auto_delink_allowed &&
+               !chip->card_ready && !chip->card_ejected && !chip->sd_io) {
+               int enter_L1 = chip->auto_delink_in_L1 && (
+                       chip->aspm_l0s_l1_en || chip->ss_en);
+               int delink_stage1_cnt = chip->delink_stage1_step;
+               int delink_stage2_cnt = delink_stage1_cnt +
+                       chip->delink_stage2_step;
+               int delink_stage3_cnt = delink_stage2_cnt +
+                       chip->delink_stage3_step;
+
+               if (chip->auto_delink_cnt <= delink_stage3_cnt) {
+                       if (chip->auto_delink_cnt == delink_stage1_cnt) {
+                               rtsx_set_stat(chip, RTSX_STAT_DELINK);
+
+                               if (chip->asic_code && CHECK_PID(chip, 0x5208))
+                                       rtsx_set_phy_reg_bit(chip, 0x1C, 2);
+
+                               if (chip->card_exist) {
+                                       RTSX_DEBUGP("False card inserted, do force delink\n");
+
+                                       if (enter_L1)
+                                               rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, 1);
+
+                                       rtsx_write_register(chip,
+                                                       CHANGE_LINK_STATE, 0x0A,
+                                                       0x0A);
+
+                                       if (enter_L1)
+                                               rtsx_enter_L1(chip);
+
+                                       chip->auto_delink_cnt = delink_stage3_cnt + 1;
+                               } else {
+                                       RTSX_DEBUGP("No card inserted, do delink\n");
+
+                                       if (enter_L1)
+                                               rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, 1);
+
+                                       rtsx_write_register(chip, CHANGE_LINK_STATE, 0x02, 0x02);
+
+                                       if (enter_L1)
+                                               rtsx_enter_L1(chip);
+
+                               }
+                       }
+
+                       if (chip->auto_delink_cnt == delink_stage2_cnt) {
+                               RTSX_DEBUGP("Try to do force delink\n");
+
+                               if (enter_L1)
+                                       rtsx_exit_L1(chip);
+
+                               if (chip->asic_code && CHECK_PID(chip, 0x5208))
+                                       rtsx_set_phy_reg_bit(chip, 0x1C, 2);
+
+                               rtsx_write_register(chip, CHANGE_LINK_STATE,
+                                               0x0A, 0x0A);
+                       }
+
+                       chip->auto_delink_cnt++;
+               }
+       } else {
+               chip->auto_delink_cnt = 0;
+       }
+}
+
+void rtsx_undo_delink(struct rtsx_chip *chip)
+{
+       chip->auto_delink_allowed = 0;
+       rtsx_write_register(chip, CHANGE_LINK_STATE, 0x0A, 0x00);
+}
+
+/**
+ * rtsx_stop_cmd - stop command transfer and DMA transfer
+ * @chip: Realtek's card reader chip
+ * @card: flash card type
+ *
+ * Stop command transfer and DMA transfer.
+ * This function is called in error handler.
+ */
+void rtsx_stop_cmd(struct rtsx_chip *chip, int card)
+{
+       int i;
+
+       for (i = 0; i <= 8; i++) {
+               int addr = RTSX_HCBAR + i * 4;
+               u32 reg;
+               reg = rtsx_readl(chip, addr);
+               RTSX_DEBUGP("BAR (0x%02x): 0x%08x\n", addr, reg);
+       }
+       rtsx_writel(chip, RTSX_HCBCTLR, STOP_CMD);
+       rtsx_writel(chip, RTSX_HDBCTLR, STOP_DMA);
+
+       for (i = 0; i < 16; i++) {
+               u16 addr = 0xFE20 + (u16)i;
+               u8 val;
+               rtsx_read_register(chip, addr, &val);
+               RTSX_DEBUGP("0x%04X: 0x%02x\n", addr, val);
+       }
+
+       rtsx_write_register(chip, DMACTL, 0x80, 0x80);
+       rtsx_write_register(chip, RBCTL, 0x80, 0x80);
+}
+
+#define MAX_RW_REG_CNT         1024
+
+int rtsx_write_register(struct rtsx_chip *chip, u16 addr, u8 mask, u8 data)
+{
+       int i;
+       u32 val = 3 << 30;
+
+       val |= (u32)(addr & 0x3FFF) << 16;
+       val |= (u32)mask << 8;
+       val |= (u32)data;
+
+       rtsx_writel(chip, RTSX_HAIMR, val);
+
+       for (i = 0; i < MAX_RW_REG_CNT; i++) {
+               val = rtsx_readl(chip, RTSX_HAIMR);
+               if ((val & (1 << 31)) == 0) {
+                       if (data != (u8)val)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       return STATUS_SUCCESS;
+               }
+       }
+
+       TRACE_RET(chip, STATUS_TIMEDOUT);
+}
+
+int rtsx_read_register(struct rtsx_chip *chip, u16 addr, u8 *data)
+{
+       u32 val = 2 << 30;
+       int i;
+
+       if (data)
+               *data = 0;
+
+       val |= (u32)(addr & 0x3FFF) << 16;
+
+       rtsx_writel(chip, RTSX_HAIMR, val);
+
+       for (i = 0; i < MAX_RW_REG_CNT; i++) {
+               val = rtsx_readl(chip, RTSX_HAIMR);
+               if ((val & (1 << 31)) == 0)
+                       break;
+       }
+
+       if (i >= MAX_RW_REG_CNT)
+               TRACE_RET(chip, STATUS_TIMEDOUT);
+
+       if (data)
+               *data = (u8)(val & 0xFF);
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_write_cfg_dw(struct rtsx_chip *chip, u8 func_no, u16 addr, u32 mask,
+               u32 val)
+{
+       u8 mode = 0, tmp;
+       int i;
+
+       for (i = 0; i < 4; i++) {
+               if (mask & 0xFF) {
+                       RTSX_WRITE_REG(chip, CFGDATA0 + i,
+                                      0xFF, (u8)(val & mask & 0xFF));
+                       mode |= (1 << i);
+               }
+               mask >>= 8;
+               val >>= 8;
+       }
+
+       if (mode) {
+               RTSX_WRITE_REG(chip, CFGADDR0, 0xFF, (u8)addr);
+               RTSX_WRITE_REG(chip, CFGADDR1, 0xFF, (u8)(addr >> 8));
+
+               RTSX_WRITE_REG(chip, CFGRWCTL, 0xFF,
+                              0x80 | mode | ((func_no & 0x03) << 4));
+
+               for (i = 0; i < MAX_RW_REG_CNT; i++) {
+                       RTSX_READ_REG(chip, CFGRWCTL, &tmp);
+                       if ((tmp & 0x80) == 0)
+                               break;
+               }
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_read_cfg_dw(struct rtsx_chip *chip, u8 func_no, u16 addr, u32 *val)
+{
+       int i;
+       u8 tmp;
+       u32 data = 0;
+
+       RTSX_WRITE_REG(chip, CFGADDR0, 0xFF, (u8)addr);
+       RTSX_WRITE_REG(chip, CFGADDR1, 0xFF, (u8)(addr >> 8));
+       RTSX_WRITE_REG(chip, CFGRWCTL, 0xFF, 0x80 | ((func_no & 0x03) << 4));
+
+       for (i = 0; i < MAX_RW_REG_CNT; i++) {
+               RTSX_READ_REG(chip, CFGRWCTL, &tmp);
+               if ((tmp & 0x80) == 0)
+                       break;
+       }
+
+       for (i = 0; i < 4; i++) {
+               RTSX_READ_REG(chip, CFGDATA0 + i, &tmp);
+               data |= (u32)tmp << (i * 8);
+       }
+
+       if (val)
+               *val = data;
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_write_cfg_seq(struct rtsx_chip *chip, u8 func, u16 addr, u8 *buf,
+               int len)
+{
+       u32 *data, *mask;
+       u16 offset = addr % 4;
+       u16 aligned_addr = addr - offset;
+       int dw_len, i, j;
+       int retval;
+
+       RTSX_DEBUGP("%s\n", __func__);
+
+       if (!buf)
+               TRACE_RET(chip, STATUS_NOMEM);
+
+       if ((len + offset) % 4)
+               dw_len = (len + offset) / 4 + 1;
+       else
+               dw_len = (len + offset) / 4;
+
+       RTSX_DEBUGP("dw_len = %d\n", dw_len);
+
+       data = vzalloc(dw_len * 4);
+       if (!data)
+               TRACE_RET(chip, STATUS_NOMEM);
+
+       mask = vzalloc(dw_len * 4);
+       if (!mask) {
+               vfree(data);
+               TRACE_RET(chip, STATUS_NOMEM);
+       }
+
+       j = 0;
+       for (i = 0; i < len; i++) {
+               mask[j] |= 0xFF << (offset * 8);
+               data[j] |= buf[i] << (offset * 8);
+               if (++offset == 4) {
+                       j++;
+                       offset = 0;
+               }
+       }
+
+       RTSX_DUMP(mask, dw_len * 4);
+       RTSX_DUMP(data, dw_len * 4);
+
+       for (i = 0; i < dw_len; i++) {
+               retval = rtsx_write_cfg_dw(chip, func, aligned_addr + i * 4,
+                                       mask[i], data[i]);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(data);
+                       vfree(mask);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       vfree(data);
+       vfree(mask);
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_read_cfg_seq(struct rtsx_chip *chip, u8 func, u16 addr, u8 *buf,
+               int len)
+{
+       u32 *data;
+       u16 offset = addr % 4;
+       u16 aligned_addr = addr - offset;
+       int dw_len, i, j;
+       int retval;
+
+       RTSX_DEBUGP("%s\n", __func__);
+
+       if ((len + offset) % 4)
+               dw_len = (len + offset) / 4 + 1;
+       else
+               dw_len = (len + offset) / 4;
+
+       RTSX_DEBUGP("dw_len = %d\n", dw_len);
+
+       data = vmalloc(dw_len * 4);
+       if (!data)
+               TRACE_RET(chip, STATUS_NOMEM);
+
+       for (i = 0; i < dw_len; i++) {
+               retval = rtsx_read_cfg_dw(chip, func, aligned_addr + i * 4,
+                                       data + i);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(data);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       if (buf) {
+               j = 0;
+
+               for (i = 0; i < len; i++) {
+                       buf[i] = (u8)(data[j] >> (offset * 8));
+                       if (++offset == 4) {
+                               j++;
+                               offset = 0;
+                       }
+               }
+       }
+
+       vfree(data);
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_write_phy_register(struct rtsx_chip *chip, u8 addr, u16 val)
+{
+       int i, finished = 0;
+       u8 tmp;
+
+       RTSX_WRITE_REG(chip, PHYDATA0, 0xFF, (u8)val);
+       RTSX_WRITE_REG(chip, PHYDATA1, 0xFF, (u8)(val >> 8));
+       RTSX_WRITE_REG(chip, PHYADDR, 0xFF, addr);
+       RTSX_WRITE_REG(chip, PHYRWCTL, 0xFF, 0x81);
+
+       for (i = 0; i < 100000; i++) {
+               RTSX_READ_REG(chip, PHYRWCTL, &tmp);
+               if (!(tmp & 0x80)) {
+                       finished = 1;
+                       break;
+               }
+       }
+
+       if (!finished)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_read_phy_register(struct rtsx_chip *chip, u8 addr, u16 *val)
+{
+       int i, finished = 0;
+       u16 data = 0;
+       u8 tmp;
+
+       RTSX_WRITE_REG(chip, PHYADDR, 0xFF, addr);
+       RTSX_WRITE_REG(chip, PHYRWCTL, 0xFF, 0x80);
+
+       for (i = 0; i < 100000; i++) {
+               RTSX_READ_REG(chip, PHYRWCTL, &tmp);
+               if (!(tmp & 0x80)) {
+                       finished = 1;
+                       break;
+               }
+       }
+
+       if (!finished)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_READ_REG(chip, PHYDATA0, &tmp);
+       data = tmp;
+       RTSX_READ_REG(chip, PHYDATA1, &tmp);
+       data |= (u16)tmp << 8;
+
+       if (val)
+               *val = data;
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_read_efuse(struct rtsx_chip *chip, u8 addr, u8 *val)
+{
+       int i;
+       u8 data = 0;
+
+       RTSX_WRITE_REG(chip, EFUSE_CTRL, 0xFF, 0x80|addr);
+
+       for (i = 0; i < 100; i++) {
+               RTSX_READ_REG(chip, EFUSE_CTRL, &data);
+               if (!(data & 0x80))
+                       break;
+               udelay(1);
+       }
+
+       if (data & 0x80)
+               TRACE_RET(chip, STATUS_TIMEDOUT);
+
+       RTSX_READ_REG(chip, EFUSE_DATA, &data);
+       if (val)
+               *val = data;
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_write_efuse(struct rtsx_chip *chip, u8 addr, u8 val)
+{
+       int i, j;
+       u8 data = 0, tmp = 0xFF;
+
+       for (i = 0; i < 8; i++) {
+               if (val & (u8)(1 << i))
+                       continue;
+
+               tmp &= (~(u8)(1 << i));
+               RTSX_DEBUGP("Write 0x%x to 0x%x\n", tmp, addr);
+
+               RTSX_WRITE_REG(chip, EFUSE_DATA, 0xFF, tmp);
+               RTSX_WRITE_REG(chip, EFUSE_CTRL, 0xFF, 0xA0|addr);
+
+               for (j = 0; j < 100; j++) {
+                       RTSX_READ_REG(chip, EFUSE_CTRL, &data);
+                       if (!(data & 0x80))
+                               break;
+                       wait_timeout(3);
+               }
+
+               if (data & 0x80)
+                       TRACE_RET(chip, STATUS_TIMEDOUT);
+
+               wait_timeout(5);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_clr_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit)
+{
+       int retval;
+       u16 value;
+
+       retval = rtsx_read_phy_register(chip, reg, &value);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (value & (1 << bit)) {
+               value &= ~(1 << bit);
+               retval = rtsx_write_phy_register(chip, reg, value);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_set_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit)
+{
+       int retval;
+       u16 value;
+
+       retval = rtsx_read_phy_register(chip, reg, &value);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (0 == (value & (1 << bit))) {
+               value |= (1 << bit);
+               retval = rtsx_write_phy_register(chip, reg, value);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_check_link_ready(struct rtsx_chip *chip)
+{
+       u8 val;
+
+       RTSX_READ_REG(chip, IRQSTAT0, &val);
+
+       RTSX_DEBUGP("IRQSTAT0: 0x%x\n", val);
+       if (val & LINK_RDY_INT) {
+               RTSX_DEBUGP("Delinked!\n");
+               rtsx_write_register(chip, IRQSTAT0, LINK_RDY_INT, LINK_RDY_INT);
+               return STATUS_FAIL;
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static void rtsx_handle_pm_dstate(struct rtsx_chip *chip, u8 dstate)
+{
+       u32 ultmp;
+
+       RTSX_DEBUGP("%04x set pm_dstate to %d\n", chip->product_id, dstate);
+
+       if (CHK_SDIO_EXIST(chip)) {
+               u8 func_no;
+
+               if (CHECK_PID(chip, 0x5288))
+                       func_no = 2;
+               else
+                       func_no = 1;
+
+               rtsx_read_cfg_dw(chip, func_no, 0x84, &ultmp);
+               RTSX_DEBUGP("pm_dstate of function %d: 0x%x\n", (int)func_no,
+                       ultmp);
+               rtsx_write_cfg_dw(chip, func_no, 0x84, 0xFF, dstate);
+       }
+
+       rtsx_write_config_byte(chip, 0x44, dstate);
+       rtsx_write_config_byte(chip, 0x45, 0);
+}
+
+void rtsx_enter_L1(struct rtsx_chip *chip)
+{
+       rtsx_handle_pm_dstate(chip, 2);
+}
+
+void rtsx_exit_L1(struct rtsx_chip *chip)
+{
+       rtsx_write_config_byte(chip, 0x44, 0);
+       rtsx_write_config_byte(chip, 0x45, 0);
+}
+
+void rtsx_enter_ss(struct rtsx_chip *chip)
+{
+       RTSX_DEBUGP("Enter Selective Suspend State!\n");
+
+       rtsx_write_register(chip, IRQSTAT0, LINK_RDY_INT, LINK_RDY_INT);
+
+       if (chip->power_down_in_ss) {
+               rtsx_power_off_card(chip);
+               rtsx_force_power_down(chip, SSC_PDCTL | OC_PDCTL);
+       }
+
+       if (CHK_SDIO_EXIST(chip)) {
+               if (CHECK_PID(chip, 0x5288))
+                       rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFF00, 0x0100);
+               else
+                       rtsx_write_cfg_dw(chip, 1, 0xC0, 0xFF00, 0x0100);
+       }
+
+       if (chip->auto_delink_en) {
+               rtsx_write_register(chip, HOST_SLEEP_STATE, 0x01, 0x01);
+       } else {
+               if (!chip->phy_debug_mode) {
+                       u32 tmp;
+                       tmp = rtsx_readl(chip, RTSX_BIER);
+                       tmp |= CARD_INT;
+                       rtsx_writel(chip, RTSX_BIER, tmp);
+               }
+
+               rtsx_write_register(chip, CHANGE_LINK_STATE, 0x02, 0);
+       }
+
+       rtsx_enter_L1(chip);
+
+       RTSX_CLR_DELINK(chip);
+       rtsx_set_stat(chip, RTSX_STAT_SS);
+}
+
+void rtsx_exit_ss(struct rtsx_chip *chip)
+{
+       RTSX_DEBUGP("Exit Selective Suspend State!\n");
+
+       rtsx_exit_L1(chip);
+
+       if (chip->power_down_in_ss) {
+               rtsx_force_power_on(chip, SSC_PDCTL | OC_PDCTL);
+               udelay(1000);
+       }
+
+       if (RTSX_TST_DELINK(chip)) {
+               chip->need_reinit = SD_CARD | MS_CARD | XD_CARD;
+               rtsx_reinit_cards(chip, 1);
+               RTSX_CLR_DELINK(chip);
+       } else if (chip->power_down_in_ss) {
+               chip->need_reinit = SD_CARD | MS_CARD | XD_CARD;
+               rtsx_reinit_cards(chip, 0);
+       }
+}
+
+int rtsx_pre_handle_interrupt(struct rtsx_chip *chip)
+{
+       u32 status, int_enable;
+       int exit_ss = 0;
+#ifdef SUPPORT_OCP
+       u32 ocp_int = 0;
+
+       ocp_int = OC_INT;
+#endif
+
+       if (chip->ss_en) {
+               chip->ss_counter = 0;
+               if (rtsx_get_stat(chip) == RTSX_STAT_SS) {
+                       exit_ss = 1;
+                       rtsx_exit_L1(chip);
+                       rtsx_set_stat(chip, RTSX_STAT_RUN);
+               }
+       }
+
+       int_enable = rtsx_readl(chip, RTSX_BIER);
+       chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
+
+       if (((chip->int_reg & int_enable) == 0) ||
+               (chip->int_reg == 0xFFFFFFFF))
+               return STATUS_FAIL;
+
+       status = chip->int_reg &= (int_enable | 0x7FFFFF);
+
+       if (status & CARD_INT) {
+               chip->auto_delink_cnt = 0;
+
+               if (status & SD_INT) {
+                       if (status & SD_EXIST) {
+                               set_bit(SD_NR, &(chip->need_reset));
+                       } else {
+                               set_bit(SD_NR, &(chip->need_release));
+                               chip->sd_reset_counter = 0;
+                               chip->sd_show_cnt = 0;
+                               clear_bit(SD_NR, &(chip->need_reset));
+                       }
+               } else {
+                       /* If multi-luns, it's possible that
+                          when plugging/unplugging one card
+                          there is another card which still
+                          exists in the slot. In this case,
+                          all existed cards should be reset.
+                       */
+                       if (exit_ss && (status & SD_EXIST))
+                               set_bit(SD_NR, &(chip->need_reinit));
+               }
+               if (!CHECK_PID(chip, 0x5288) || CHECK_BARO_PKG(chip, QFN)) {
+                       if (status & XD_INT) {
+                               if (status & XD_EXIST) {
+                                       set_bit(XD_NR, &(chip->need_reset));
+                               } else {
+                                       set_bit(XD_NR, &(chip->need_release));
+                                       chip->xd_reset_counter = 0;
+                                       chip->xd_show_cnt = 0;
+                                       clear_bit(XD_NR, &(chip->need_reset));
+                               }
+                       } else {
+                               if (exit_ss && (status & XD_EXIST))
+                                       set_bit(XD_NR, &(chip->need_reinit));
+                       }
+               }
+               if (status & MS_INT) {
+                       if (status & MS_EXIST) {
+                               set_bit(MS_NR, &(chip->need_reset));
+                       } else {
+                               set_bit(MS_NR, &(chip->need_release));
+                               chip->ms_reset_counter = 0;
+                               chip->ms_show_cnt = 0;
+                               clear_bit(MS_NR, &(chip->need_reset));
+                       }
+               } else {
+                       if (exit_ss && (status & MS_EXIST))
+                               set_bit(MS_NR, &(chip->need_reinit));
+               }
+       }
+
+#ifdef SUPPORT_OCP
+       chip->ocp_int = ocp_int & status;
+#endif
+
+       if (chip->sd_io) {
+               if (chip->int_reg & DATA_DONE_INT)
+                       chip->int_reg &= ~(u32)DATA_DONE_INT;
+       }
+
+       return STATUS_SUCCESS;
+}
+
+void rtsx_do_before_power_down(struct rtsx_chip *chip, int pm_stat)
+{
+       int retval;
+
+       RTSX_DEBUGP("rtsx_do_before_power_down, pm_stat = %d\n", pm_stat);
+
+       rtsx_set_stat(chip, RTSX_STAT_SUSPEND);
+
+       retval = rtsx_force_power_on(chip, SSC_PDCTL);
+       if (retval != STATUS_SUCCESS)
+               return;
+
+       rtsx_release_cards(chip);
+       rtsx_disable_bus_int(chip);
+       turn_off_led(chip, LED_GPIO);
+
+#ifdef HW_AUTO_SWITCH_SD_BUS
+       if (chip->sd_io) {
+               chip->sdio_in_charge = 1;
+               if (CHECK_PID(chip, 0x5208)) {
+                       rtsx_write_register(chip, TLPTISTAT, 0x08, 0x08);
+                       /* Enable sdio_bus_auto_switch */
+                       rtsx_write_register(chip, 0xFE70, 0x80, 0x80);
+               } else if (CHECK_PID(chip, 0x5288)) {
+                       rtsx_write_register(chip, TLPTISTAT, 0x08, 0x08);
+                       /* Enable sdio_bus_auto_switch */
+                       rtsx_write_register(chip, 0xFE5A, 0x08, 0x08);
+               }
+       }
+#endif
+
+       if (CHECK_PID(chip, 0x5208) && (chip->ic_version >= IC_VER_D)) {
+               /* u_force_clkreq_0 */
+               rtsx_write_register(chip, PETXCFG, 0x08, 0x08);
+       }
+
+       if (pm_stat == PM_S1) {
+               RTSX_DEBUGP("Host enter S1\n");
+               rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03,
+                               HOST_ENTER_S1);
+       } else if (pm_stat == PM_S3) {
+               if (chip->s3_pwr_off_delay > 0)
+                       wait_timeout(chip->s3_pwr_off_delay);
+
+               RTSX_DEBUGP("Host enter S3\n");
+               rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03,
+                               HOST_ENTER_S3);
+       }
+
+       if (chip->do_delink_before_power_down && chip->auto_delink_en)
+               rtsx_write_register(chip, CHANGE_LINK_STATE, 0x02, 2);
+
+       rtsx_force_power_down(chip, SSC_PDCTL | OC_PDCTL);
+
+       chip->cur_clk = 0;
+       chip->cur_card = 0;
+       chip->card_exist = 0;
+}
+
+void rtsx_enable_aspm(struct rtsx_chip *chip)
+{
+       if (chip->aspm_l0s_l1_en && chip->dynamic_aspm) {
+               if (!chip->aspm_enabled) {
+                       RTSX_DEBUGP("Try to enable ASPM\n");
+                       chip->aspm_enabled = 1;
+
+                       if (chip->asic_code && CHECK_PID(chip, 0x5208))
+                               rtsx_write_phy_register(chip, 0x07, 0);
+                       if (CHECK_PID(chip, 0x5208)) {
+                               rtsx_write_register(chip, ASPM_FORCE_CTL, 0xF3,
+                                       0x30 | chip->aspm_level[0]);
+                       } else {
+                               rtsx_write_config_byte(chip, LCTLR,
+                                               chip->aspm_l0s_l1_en);
+                       }
+
+                       if (CHK_SDIO_EXIST(chip)) {
+                               u16 val = chip->aspm_l0s_l1_en | 0x0100;
+                               if (CHECK_PID(chip, 0x5288))
+                                       rtsx_write_cfg_dw(chip, 2, 0xC0,
+                                                       0xFFFF, val);
+                               else
+                                       rtsx_write_cfg_dw(chip, 1, 0xC0,
+                                                       0xFFFF, val);
+                       }
+               }
+       }
+
+       return;
+}
+
+void rtsx_disable_aspm(struct rtsx_chip *chip)
+{
+       if (CHECK_PID(chip, 0x5208))
+               rtsx_monitor_aspm_config(chip);
+
+       if (chip->aspm_l0s_l1_en && chip->dynamic_aspm) {
+               if (chip->aspm_enabled) {
+                       RTSX_DEBUGP("Try to disable ASPM\n");
+                       chip->aspm_enabled = 0;
+
+                       if (chip->asic_code && CHECK_PID(chip, 0x5208))
+                               rtsx_write_phy_register(chip, 0x07, 0x0129);
+                       if (CHECK_PID(chip, 0x5208))
+                               rtsx_write_register(chip, ASPM_FORCE_CTL,
+                                               0xF3, 0x30);
+                       else
+                               rtsx_write_config_byte(chip, LCTLR, 0x00);
+
+                       wait_timeout(1);
+               }
+       }
+
+       return;
+}
+
+int rtsx_read_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len)
+{
+       int retval;
+       int i, j;
+       u16 reg_addr;
+       u8 *ptr;
+
+       if (!buf)
+               TRACE_RET(chip, STATUS_ERROR);
+
+       ptr = buf;
+       reg_addr = PPBUF_BASE2;
+       for (i = 0; i < buf_len/256; i++) {
+               rtsx_init_cmd(chip);
+
+               for (j = 0; j < 256; j++)
+                       rtsx_add_cmd(chip, READ_REG_CMD, reg_addr++, 0, 0);
+
+               retval = rtsx_send_cmd(chip, 0, 250);
+               if (retval < 0)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               memcpy(ptr, rtsx_get_cmd_data(chip), 256);
+               ptr += 256;
+       }
+
+       if (buf_len%256) {
+               rtsx_init_cmd(chip);
+
+               for (j = 0; j < buf_len%256; j++)
+                       rtsx_add_cmd(chip, READ_REG_CMD, reg_addr++, 0, 0);
+
+               retval = rtsx_send_cmd(chip, 0, 250);
+               if (retval < 0)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       memcpy(ptr, rtsx_get_cmd_data(chip), buf_len%256);
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_write_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len)
+{
+       int retval;
+       int i, j;
+       u16 reg_addr;
+       u8 *ptr;
+
+       if (!buf)
+               TRACE_RET(chip, STATUS_ERROR);
+
+       ptr = buf;
+       reg_addr = PPBUF_BASE2;
+       for (i = 0; i < buf_len/256; i++) {
+               rtsx_init_cmd(chip);
+
+               for (j = 0; j < 256; j++) {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, reg_addr++, 0xFF,
+                               *ptr);
+                       ptr++;
+               }
+
+               retval = rtsx_send_cmd(chip, 0, 250);
+               if (retval < 0)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (buf_len%256) {
+               rtsx_init_cmd(chip);
+
+               for (j = 0; j < buf_len%256; j++) {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, reg_addr++, 0xFF,
+                               *ptr);
+                       ptr++;
+               }
+
+               retval = rtsx_send_cmd(chip, 0, 250);
+               if (retval < 0)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_check_chip_exist(struct rtsx_chip *chip)
+{
+       if (rtsx_readl(chip, 0) == 0xFFFFFFFF)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_force_power_on(struct rtsx_chip *chip, u8 ctl)
+{
+       int retval;
+       u8 mask = 0;
+
+       if (ctl & SSC_PDCTL)
+               mask |= SSC_POWER_DOWN;
+
+#ifdef SUPPORT_OCP
+       if (ctl & OC_PDCTL) {
+               mask |= SD_OC_POWER_DOWN;
+               if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
+                       mask |= MS_OC_POWER_DOWN;
+       }
+#endif
+
+       if (mask) {
+               retval = rtsx_write_register(chip, FPDCTL, mask, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if (CHECK_PID(chip, 0x5288))
+                       wait_timeout(200);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int rtsx_force_power_down(struct rtsx_chip *chip, u8 ctl)
+{
+       int retval;
+       u8 mask = 0, val = 0;
+
+       if (ctl & SSC_PDCTL)
+               mask |= SSC_POWER_DOWN;
+
+#ifdef SUPPORT_OCP
+       if (ctl & OC_PDCTL) {
+               mask |= SD_OC_POWER_DOWN;
+               if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
+                       mask |= MS_OC_POWER_DOWN;
+       }
+#endif
+
+       if (mask) {
+               val = mask;
+               retval = rtsx_write_register(chip, FPDCTL, mask, val);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
diff --git a/drivers/staging/rts5208/rtsx_chip.h b/drivers/staging/rts5208/rtsx_chip.h
new file mode 100644 (file)
index 0000000..c25efcc
--- /dev/null
@@ -0,0 +1,1002 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_CHIP_H
+#define __REALTEK_RTSX_CHIP_H
+
+#include "rtsx.h"
+
+#define SUPPORT_CPRM
+#define SUPPORT_OCP
+#define SUPPORT_SDIO_ASPM
+#define SUPPORT_MAGIC_GATE
+#define SUPPORT_MSXC
+#define SUPPORT_SD_LOCK
+/* Hardware switch bus_ctl and cd_ctl automatically */
+#define HW_AUTO_SWITCH_SD_BUS
+/* Enable hardware interrupt write clear */
+#define HW_INT_WRITE_CLR
+/* #define LED_AUTO_BLINK */
+/* #define DISABLE_CARD_INT */
+
+#ifdef SUPPORT_MAGIC_GATE
+       /* Using NORMAL_WRITE instead of AUTO_WRITE to set ICV */
+       #define MG_SET_ICV_SLOW
+       /* HW may miss ERR/CMDNK signal when sampling INT status. */
+       #define MS_SAMPLE_INT_ERR
+       /* HW DO NOT support Wait_INT function during READ_BYTES
+        * transfer mode */
+       #define READ_BYTES_WAIT_INT
+#endif
+
+#ifdef SUPPORT_MSXC
+#define XC_POWERCLASS
+#define SUPPORT_PCGL_1P18
+#endif
+
+#ifndef LED_AUTO_BLINK
+#define REGULAR_BLINK
+#endif
+
+#define LED_BLINK_SPEED                5
+#define LED_TOGGLE_INTERVAL    6
+#define        GPIO_TOGGLE_THRESHOLD   1024
+#define LED_GPIO               0
+
+#define POLLING_INTERVAL       30
+
+#define TRACE_ITEM_CNT         64
+
+#ifndef STATUS_SUCCESS
+#define STATUS_SUCCESS         0
+#endif
+#ifndef STATUS_FAIL
+#define STATUS_FAIL            1
+#endif
+#ifndef STATUS_TIMEDOUT
+#define STATUS_TIMEDOUT                2
+#endif
+#ifndef STATUS_NOMEM
+#define STATUS_NOMEM           3
+#endif
+#ifndef STATUS_READ_FAIL
+#define STATUS_READ_FAIL       4
+#endif
+#ifndef STATUS_WRITE_FAIL
+#define STATUS_WRITE_FAIL      5
+#endif
+#ifndef STATUS_ERROR
+#define STATUS_ERROR           10
+#endif
+
+#define PM_S1                  1
+#define PM_S3                  3
+
+/*
+ * Transport return codes
+ */
+
+#define TRANSPORT_GOOD         0   /* Transport good, command good        */
+#define TRANSPORT_FAILED       1   /* Transport good, command failed   */
+#define TRANSPORT_NO_SENSE     2  /* Command failed, no auto-sense    */
+#define TRANSPORT_ERROR                3   /* Transport bad (i.e. device dead) */
+
+
+/*-----------------------------------
+    Start-Stop-Unit
+-----------------------------------*/
+#define STOP_MEDIUM                    0x00    /* access disable         */
+#define MAKE_MEDIUM_READY              0x01    /* access enable          */
+#define UNLOAD_MEDIUM                  0x02    /* unload                 */
+#define LOAD_MEDIUM                    0x03    /* load                   */
+
+/*-----------------------------------
+    STANDARD_INQUIRY
+-----------------------------------*/
+#define QULIFIRE                0x00
+#define AENC_FNC                0x00
+#define TRML_IOP                0x00
+#define REL_ADR                 0x00
+#define WBUS_32                 0x00
+#define WBUS_16                 0x00
+#define SYNC                    0x00
+#define LINKED                  0x00
+#define CMD_QUE                 0x00
+#define SFT_RE                  0x00
+
+#define VEN_ID_LEN              8               /* Vendor ID Length         */
+#define PRDCT_ID_LEN            16              /* Product ID Length        */
+#define PRDCT_REV_LEN           4               /* Product LOT Length       */
+
+/* Dynamic flag definitions: used in set_bit() etc. */
+#define RTSX_FLIDX_TRANS_ACTIVE                18  /* 0x00040000  transfer is active */
+#define RTSX_FLIDX_ABORTING            20  /* 0x00100000 abort is in
+                                            * progress */
+#define RTSX_FLIDX_DISCONNECTING       21  /* 0x00200000 disconnect
+                                            * in progress */
+#define ABORTING_OR_DISCONNECTING      ((1UL << US_FLIDX_ABORTING) | \
+                                        (1UL << US_FLIDX_DISCONNECTING))
+#define RTSX_FLIDX_RESETTING           22  /* 0x00400000 device reset
+                                            * in progress */
+#define RTSX_FLIDX_TIMED_OUT           23  /* 0x00800000 SCSI
+                                            * midlayer timed out */
+
+#define DRCT_ACCESS_DEV         0x00    /* Direct Access Device      */
+#define RMB_DISC                0x80    /* The Device is Removable   */
+#define ANSI_SCSI2              0x02    /* Based on ANSI-SCSI2       */
+
+#define SCSI                    0x00    /* Interface ID              */
+
+#define        WRITE_PROTECTED_MEDIA 0x07
+
+/*---- sense key ----*/
+#define ILI                     0x20    /* ILI bit is on                    */
+
+#define NO_SENSE                0x00    /* not exist sense key              */
+#define RECOVER_ERR             0x01    /* Target/Logical unit is recoverd  */
+#define NOT_READY               0x02    /* Logical unit is not ready        */
+#define MEDIA_ERR               0x03    /* medium/data error                */
+#define HARDWARE_ERR            0x04    /* hardware error                   */
+#define ILGAL_REQ               0x05    /* CDB/parameter/identify msg error */
+#define UNIT_ATTENTION          0x06    /* unit attention condition occur   */
+#define DAT_PRTCT               0x07    /* read/write is desable            */
+#define BLNC_CHK                0x08    /* find blank/DOF in read           */
+                                       /* write to unblank area            */
+#define CPY_ABRT                0x0a    /* Copy/Compare/Copy&Verify illgal  */
+#define ABRT_CMD                0x0b    /* Target make the command in error */
+#define EQUAL                   0x0c    /* Search Data end with Equal       */
+#define VLM_OVRFLW              0x0d    /* Some data are left in buffer     */
+#define MISCMP                  0x0e    /* find inequality                  */
+
+#define READ_ERR                -1
+#define WRITE_ERR               -2
+
+#define        FIRST_RESET             0x01
+#define        USED_EXIST              0x02
+
+/*-----------------------------------
+    SENSE_DATA
+-----------------------------------*/
+/*---- valid ----*/
+#define SENSE_VALID             0x80    /* Sense data is valid as SCSI2     */
+#define SENSE_INVALID           0x00    /* Sense data is invalid as SCSI2   */
+
+/*---- error code ----*/
+#define CUR_ERR                 0x70    /* current error                    */
+#define DEF_ERR                 0x71    /* specific command error           */
+
+/*---- sense key Information ----*/
+#define SNSKEYINFO_LEN          3       /* length of sense key information   */
+
+#define SKSV                    0x80
+#define CDB_ILLEGAL             0x40
+#define DAT_ILLEGAL             0x00
+#define BPV                     0x08
+#define BIT_ILLEGAL0            0       /* bit0 is illegal                  */
+#define BIT_ILLEGAL1            1       /* bit1 is illegal                  */
+#define BIT_ILLEGAL2            2       /* bit2 is illegal                  */
+#define BIT_ILLEGAL3            3       /* bit3 is illegal                  */
+#define BIT_ILLEGAL4            4       /* bit4 is illegal                  */
+#define BIT_ILLEGAL5            5       /* bit5 is illegal                  */
+#define BIT_ILLEGAL6            6       /* bit6 is illegal                  */
+#define BIT_ILLEGAL7            7       /* bit7 is illegal                  */
+
+/*---- ASC ----*/
+#define ASC_NO_INFO             0x00
+#define ASC_MISCMP              0x1d
+#define ASC_INVLD_CDB           0x24
+#define ASC_INVLD_PARA          0x26
+#define ASC_LU_NOT_READY       0x04
+#define ASC_WRITE_ERR           0x0c
+#define ASC_READ_ERR            0x11
+#define ASC_LOAD_EJCT_ERR       0x53
+#define        ASC_MEDIA_NOT_PRESENT   0x3A
+#define        ASC_MEDIA_CHANGED       0x28
+#define        ASC_MEDIA_IN_PROCESS    0x04
+#define        ASC_WRITE_PROTECT       0x27
+#define ASC_LUN_NOT_SUPPORTED  0x25
+
+/*---- ASQC ----*/
+#define ASCQ_NO_INFO            0x00
+#define        ASCQ_MEDIA_IN_PROCESS   0x01
+#define ASCQ_MISCMP             0x00
+#define ASCQ_INVLD_CDB          0x00
+#define ASCQ_INVLD_PARA         0x02
+#define ASCQ_LU_NOT_READY      0x02
+#define ASCQ_WRITE_ERR          0x02
+#define ASCQ_READ_ERR           0x00
+#define ASCQ_LOAD_EJCT_ERR      0x00
+#define        ASCQ_WRITE_PROTECT      0x00
+
+
+struct sense_data_t {
+       unsigned char   err_code;       /* error code */
+       /* bit7 : valid */
+       /*   (1 : SCSI2) */
+       /*   (0 : Vendor * specific) */
+       /* bit6-0 : error * code */
+       /*  (0x70 : current * error) */
+       /*  (0x71 : specific command error) */
+       unsigned char   seg_no;         /* segment No.                      */
+       unsigned char   sense_key;      /* byte5 : ILI                      */
+       /* bit3-0 : sense key              */
+       unsigned char   info[4];        /* information                       */
+       unsigned char   ad_sense_len;   /* additional sense data length     */
+       unsigned char   cmd_info[4];    /* command specific information      */
+       unsigned char   asc;            /* ASC                              */
+       unsigned char   ascq;           /* ASCQ                             */
+       unsigned char   rfu;            /* FRU                              */
+       unsigned char   sns_key_info[3];/* sense key specific information    */
+};
+
+/* PCI Operation Register Address */
+#define RTSX_HCBAR             0x00
+#define RTSX_HCBCTLR           0x04
+#define RTSX_HDBAR             0x08
+#define RTSX_HDBCTLR           0x0C
+#define RTSX_HAIMR             0x10
+#define RTSX_BIPR              0x14
+#define RTSX_BIER              0x18
+
+/* Host command buffer control register */
+#define STOP_CMD               (0x01 << 28)
+
+/* Host data buffer control register */
+#define SDMA_MODE              0x00
+#define ADMA_MODE              (0x02 << 26)
+#define STOP_DMA               (0x01 << 28)
+#define TRIG_DMA               (0x01 << 31)
+
+/* Bus interrupt pending register */
+#define CMD_DONE_INT           (1 << 31)
+#define DATA_DONE_INT          (1 << 30)
+#define TRANS_OK_INT           (1 << 29)
+#define TRANS_FAIL_INT         (1 << 28)
+#define XD_INT                 (1 << 27)
+#define MS_INT                 (1 << 26)
+#define SD_INT                 (1 << 25)
+#define GPIO0_INT              (1 << 24)
+#define OC_INT                 (1 << 23)
+#define SD_WRITE_PROTECT       (1 << 19)
+#define XD_EXIST               (1 << 18)
+#define MS_EXIST               (1 << 17)
+#define SD_EXIST               (1 << 16)
+#define DELINK_INT             GPIO0_INT
+#define MS_OC_INT              (1 << 23)
+#define SD_OC_INT              (1 << 22)
+
+#define CARD_INT               (XD_INT | MS_INT | SD_INT)
+#define NEED_COMPLETE_INT      (DATA_DONE_INT | TRANS_OK_INT | TRANS_FAIL_INT)
+#define RTSX_INT               (CMD_DONE_INT | NEED_COMPLETE_INT | CARD_INT | GPIO0_INT | OC_INT)
+
+#define CARD_EXIST             (XD_EXIST | MS_EXIST | SD_EXIST)
+
+/* Bus interrupt enable register */
+#define CMD_DONE_INT_EN                (1 << 31)
+#define DATA_DONE_INT_EN       (1 << 30)
+#define TRANS_OK_INT_EN                (1 << 29)
+#define TRANS_FAIL_INT_EN      (1 << 28)
+#define XD_INT_EN              (1 << 27)
+#define MS_INT_EN              (1 << 26)
+#define SD_INT_EN              (1 << 25)
+#define GPIO0_INT_EN           (1 << 24)
+#define OC_INT_EN              (1 << 23)
+#define DELINK_INT_EN          GPIO0_INT_EN
+#define MS_OC_INT_EN           (1 << 23)
+#define SD_OC_INT_EN           (1 << 22)
+
+
+#define READ_REG_CMD           0
+#define WRITE_REG_CMD          1
+#define CHECK_REG_CMD          2
+
+#define HOST_TO_DEVICE         0
+#define DEVICE_TO_HOST         1
+
+
+#define RTSX_RESV_BUF_LEN      4096
+#define HOST_CMDS_BUF_LEN      1024
+#define HOST_SG_TBL_BUF_LEN    (RTSX_RESV_BUF_LEN - HOST_CMDS_BUF_LEN)
+
+#define SD_NR          2
+#define MS_NR          3
+#define XD_NR          4
+#define SPI_NR         7
+#define SD_CARD                (1 << SD_NR)
+#define MS_CARD                (1 << MS_NR)
+#define XD_CARD                (1 << XD_NR)
+#define SPI_CARD       (1 << SPI_NR)
+
+#define MAX_ALLOWED_LUN_CNT    8
+
+#define XD_FREE_TABLE_CNT      1200
+#define MS_FREE_TABLE_CNT      512
+
+
+/* Bit Operation */
+#define SET_BIT(data, idx)     ((data) |= 1 << (idx))
+#define CLR_BIT(data, idx)     ((data) &= ~(1 << (idx)))
+#define CHK_BIT(data, idx)     ((data) & (1 << (idx)))
+
+/* SG descriptor */
+#define SG_INT                 0x04
+#define SG_END                 0x02
+#define SG_VALID               0x01
+
+#define SG_NO_OP               0x00
+#define SG_TRANS_DATA          (0x02 << 4)
+#define SG_LINK_DESC           (0x03 << 4)
+
+struct rtsx_chip;
+
+typedef int (*card_rw_func)(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+                       u32 sec_addr, u16 sec_cnt);
+
+/* Supported Clock */
+enum card_clock        {CLK_20 = 1, CLK_30, CLK_40, CLK_50, CLK_60,
+                CLK_80, CLK_100, CLK_120, CLK_150, CLK_200};
+
+enum RTSX_STAT {RTSX_STAT_INIT, RTSX_STAT_IDLE, RTSX_STAT_RUN, RTSX_STAT_SS,
+                RTSX_STAT_DELINK, RTSX_STAT_SUSPEND,
+                RTSX_STAT_ABORT, RTSX_STAT_DISCONNECT};
+enum IC_VER    {IC_VER_AB, IC_VER_C = 2, IC_VER_D = 3};
+
+#define MAX_RESET_CNT          3
+
+/* For MS Card */
+#define MAX_DEFECTIVE_BLOCK     10
+
+struct zone_entry {
+       u16 *l2p_table;
+       u16 *free_table;
+       u16 defect_list[MAX_DEFECTIVE_BLOCK];  /* For MS card only */
+       int set_index;
+       int get_index;
+       int unused_blk_cnt;
+       int disable_count;
+       /* To indicate whether the L2P table of this zone has been built. */
+       int build_flag;
+};
+
+#define TYPE_SD                        0x0000
+#define TYPE_MMC               0x0001
+
+/* TYPE_SD */
+#define SD_HS                  0x0100
+#define SD_SDR50               0x0200
+#define SD_DDR50               0x0400
+#define SD_SDR104              0x0800
+#define SD_HCXC                        0x1000
+
+/* TYPE_MMC */
+#define MMC_26M                        0x0100
+#define MMC_52M                        0x0200
+#define MMC_4BIT               0x0400
+#define MMC_8BIT               0x0800
+#define MMC_SECTOR_MODE                0x1000
+#define MMC_DDR52              0x2000
+
+/* SD card */
+#define CHK_SD(sd_card)                        (((sd_card)->sd_type & 0xFF) == TYPE_SD)
+#define CHK_SD_HS(sd_card)             (CHK_SD(sd_card) && ((sd_card)->sd_type & SD_HS))
+#define CHK_SD_SDR50(sd_card)          (CHK_SD(sd_card) && ((sd_card)->sd_type & SD_SDR50))
+#define CHK_SD_DDR50(sd_card)          (CHK_SD(sd_card) && ((sd_card)->sd_type & SD_DDR50))
+#define CHK_SD_SDR104(sd_card)         (CHK_SD(sd_card) && ((sd_card)->sd_type & SD_SDR104))
+#define CHK_SD_HCXC(sd_card)           (CHK_SD(sd_card) && ((sd_card)->sd_type & SD_HCXC))
+#define CHK_SD_HC(sd_card)             (CHK_SD_HCXC(sd_card) && ((sd_card)->capacity <= 0x4000000))
+#define CHK_SD_XC(sd_card)             (CHK_SD_HCXC(sd_card) && ((sd_card)->capacity > 0x4000000))
+#define CHK_SD30_SPEED(sd_card)                (CHK_SD_SDR50(sd_card) || CHK_SD_DDR50(sd_card) || CHK_SD_SDR104(sd_card))
+
+#define SET_SD(sd_card)                        ((sd_card)->sd_type = TYPE_SD)
+#define SET_SD_HS(sd_card)             ((sd_card)->sd_type |= SD_HS)
+#define SET_SD_SDR50(sd_card)          ((sd_card)->sd_type |= SD_SDR50)
+#define SET_SD_DDR50(sd_card)          ((sd_card)->sd_type |= SD_DDR50)
+#define SET_SD_SDR104(sd_card)         ((sd_card)->sd_type |= SD_SDR104)
+#define SET_SD_HCXC(sd_card)           ((sd_card)->sd_type |= SD_HCXC)
+
+#define CLR_SD_HS(sd_card)             ((sd_card)->sd_type &= ~SD_HS)
+#define CLR_SD_SDR50(sd_card)          ((sd_card)->sd_type &= ~SD_SDR50)
+#define CLR_SD_DDR50(sd_card)          ((sd_card)->sd_type &= ~SD_DDR50)
+#define CLR_SD_SDR104(sd_card)         ((sd_card)->sd_type &= ~SD_SDR104)
+#define CLR_SD_HCXC(sd_card)           ((sd_card)->sd_type &= ~SD_HCXC)
+
+/* MMC card */
+#define CHK_MMC(sd_card)               (((sd_card)->sd_type & 0xFF) == TYPE_MMC)
+#define CHK_MMC_26M(sd_card)           (CHK_MMC(sd_card) && ((sd_card)->sd_type & MMC_26M))
+#define CHK_MMC_52M(sd_card)           (CHK_MMC(sd_card) && ((sd_card)->sd_type & MMC_52M))
+#define CHK_MMC_4BIT(sd_card)          (CHK_MMC(sd_card) && ((sd_card)->sd_type & MMC_4BIT))
+#define CHK_MMC_8BIT(sd_card)          (CHK_MMC(sd_card) && ((sd_card)->sd_type & MMC_8BIT))
+#define CHK_MMC_SECTOR_MODE(sd_card)   (CHK_MMC(sd_card) && ((sd_card)->sd_type & MMC_SECTOR_MODE))
+#define CHK_MMC_DDR52(sd_card)         (CHK_MMC(sd_card) && ((sd_card)->sd_type & MMC_DDR52))
+
+#define SET_MMC(sd_card)               ((sd_card)->sd_type = TYPE_MMC)
+#define SET_MMC_26M(sd_card)           ((sd_card)->sd_type |= MMC_26M)
+#define SET_MMC_52M(sd_card)           ((sd_card)->sd_type |= MMC_52M)
+#define SET_MMC_4BIT(sd_card)          ((sd_card)->sd_type |= MMC_4BIT)
+#define SET_MMC_8BIT(sd_card)          ((sd_card)->sd_type |= MMC_8BIT)
+#define SET_MMC_SECTOR_MODE(sd_card)   ((sd_card)->sd_type |= MMC_SECTOR_MODE)
+#define SET_MMC_DDR52(sd_card)         ((sd_card)->sd_type |= MMC_DDR52)
+
+#define CLR_MMC_26M(sd_card)           ((sd_card)->sd_type &= ~MMC_26M)
+#define CLR_MMC_52M(sd_card)           ((sd_card)->sd_type &= ~MMC_52M)
+#define CLR_MMC_4BIT(sd_card)          ((sd_card)->sd_type &= ~MMC_4BIT)
+#define CLR_MMC_8BIT(sd_card)          ((sd_card)->sd_type &= ~MMC_8BIT)
+#define CLR_MMC_SECTOR_MODE(sd_card)   ((sd_card)->sd_type &= ~MMC_SECTOR_MODE)
+#define CLR_MMC_DDR52(sd_card)         ((sd_card)->sd_type &= ~MMC_DDR52)
+
+#define CHK_MMC_HS(sd_card)            (CHK_MMC_52M(sd_card) && CHK_MMC_26M(sd_card))
+#define CLR_MMC_HS(sd_card)                    \
+do {                                           \
+       CLR_MMC_DDR52(sd_card);                 \
+       CLR_MMC_52M(sd_card);                   \
+       CLR_MMC_26M(sd_card);                   \
+} while (0)
+
+#define SD_SUPPORT_CLASS_TEN           0x01
+#define SD_SUPPORT_1V8                 0x02
+
+#define SD_SET_CLASS_TEN(sd_card)      ((sd_card)->sd_setting |= SD_SUPPORT_CLASS_TEN)
+#define SD_CHK_CLASS_TEN(sd_card)      ((sd_card)->sd_setting & SD_SUPPORT_CLASS_TEN)
+#define SD_CLR_CLASS_TEN(sd_card)      ((sd_card)->sd_setting &= ~SD_SUPPORT_CLASS_TEN)
+#define SD_SET_1V8(sd_card)            ((sd_card)->sd_setting |= SD_SUPPORT_1V8)
+#define SD_CHK_1V8(sd_card)            ((sd_card)->sd_setting & SD_SUPPORT_1V8)
+#define SD_CLR_1V8(sd_card)            ((sd_card)->sd_setting &= ~SD_SUPPORT_1V8)
+
+struct sd_info {
+       u16 sd_type;
+       u8 err_code;
+       u8 sd_data_buf_ready;
+       u32 sd_addr;
+       u32 capacity;
+
+       u8 raw_csd[16];
+       u8 raw_scr[8];
+
+       /* Sequential RW */
+       int seq_mode;
+       enum dma_data_direction pre_dir;
+       u32 pre_sec_addr;
+       u16 pre_sec_cnt;
+
+       int cleanup_counter;
+
+       int sd_clock;
+
+       int mmc_dont_switch_bus;
+
+#ifdef SUPPORT_CPRM
+       int sd_pass_thru_en;
+       int pre_cmd_err;
+       u8 last_rsp_type;
+       u8 rsp[17];
+#endif
+
+       u8 func_group1_mask;
+       u8 func_group2_mask;
+       u8 func_group3_mask;
+       u8 func_group4_mask;
+
+       u8 sd_switch_fail;
+       u8 sd_read_phase;
+
+#ifdef SUPPORT_SD_LOCK
+       u8 sd_lock_status;
+       u8 sd_erase_status;
+       u8 sd_lock_notify;
+#endif
+       int need_retune;
+};
+
+struct xd_delay_write_tag {
+       u32 old_phyblock;
+       u32 new_phyblock;
+       u32 logblock;
+       u8 pageoff;
+       u8 delay_write_flag;
+};
+
+struct xd_info {
+       u8 maker_code;
+       u8 device_code;
+       u8 block_shift;
+       u8 page_off;
+       u8 addr_cycle;
+       u16 cis_block;
+       u8 multi_flag;
+       u8 err_code;
+       u32 capacity;
+
+       struct zone_entry *zone;
+       int zone_cnt;
+
+       struct xd_delay_write_tag delay_write;
+       int cleanup_counter;
+
+       int xd_clock;
+};
+
+#define MODE_512_SEQ           0x01
+#define MODE_2K_SEQ            0x02
+
+#define TYPE_MS                        0x0000
+#define TYPE_MSPRO             0x0001
+
+#define MS_4BIT                        0x0100
+#define MS_8BIT                        0x0200
+#define MS_HG                  0x0400
+#define MS_XC                  0x0800
+
+#define HG8BIT                 (MS_HG | MS_8BIT)
+
+#define CHK_MSPRO(ms_card)     (((ms_card)->ms_type & 0xFF) == TYPE_MSPRO)
+#define CHK_HG8BIT(ms_card)    (CHK_MSPRO(ms_card) && (((ms_card)->ms_type & HG8BIT) == HG8BIT))
+#define CHK_MSXC(ms_card)      (CHK_MSPRO(ms_card) && ((ms_card)->ms_type & MS_XC))
+#define CHK_MSHG(ms_card)      (CHK_MSPRO(ms_card) && ((ms_card)->ms_type & MS_HG))
+
+#define CHK_MS8BIT(ms_card)    (((ms_card)->ms_type & MS_8BIT))
+#define CHK_MS4BIT(ms_card)    (((ms_card)->ms_type & MS_4BIT))
+
+struct ms_delay_write_tag {
+       u16 old_phyblock;
+       u16 new_phyblock;
+       u16 logblock;
+       u8 pageoff;
+       u8 delay_write_flag;
+};
+
+struct ms_info {
+       u16 ms_type;
+       u8 block_shift;
+       u8 page_off;
+       u16 total_block;
+       u16 boot_block;
+       u32 capacity;
+
+       u8 check_ms_flow;
+       u8 switch_8bit_fail;
+       u8 err_code;
+
+       struct zone_entry *segment;
+       int segment_cnt;
+
+       int pro_under_formatting;
+       int format_status;
+       u16 progress;
+       u8 raw_sys_info[96];
+#ifdef SUPPORT_PCGL_1P18
+       u8 raw_model_name[48];
+#endif
+
+       u8 multi_flag;
+
+       /* Sequential RW */
+       u8 seq_mode;
+       enum dma_data_direction pre_dir;
+       u32 pre_sec_addr;
+       u16 pre_sec_cnt;
+       u32 total_sec_cnt;
+
+       struct ms_delay_write_tag delay_write;
+
+       int cleanup_counter;
+
+       int ms_clock;
+
+#ifdef SUPPORT_MAGIC_GATE
+       u8 magic_gate_id[16];
+       u8 mg_entry_num;
+       int mg_auth;    /* flag to indicate authentication process */
+#endif
+};
+
+struct spi_info {
+       u8 use_clk;
+       u8 write_en;
+       u16 clk_div;
+       u8 err_code;
+
+       int spi_clock;
+};
+
+
+#ifdef _MSG_TRACE
+struct trace_msg_t {
+       u16 line;
+#define MSG_FUNC_LEN 64
+       char func[MSG_FUNC_LEN];
+#define MSG_FILE_LEN 32
+       char file[MSG_FILE_LEN];
+#define TIME_VAL_LEN 16
+       u8 timeval_buf[TIME_VAL_LEN];
+       u8 valid;
+};
+#endif
+
+/************/
+/* LUN mode */
+/************/
+/* Single LUN, support xD/SD/MS */
+#define DEFAULT_SINGLE         0
+/* 2 LUN mode, support SD/MS */
+#define SD_MS_2LUN             1
+/* Single LUN, but only support SD/MS, for Barossa LQFP */
+#define SD_MS_1LUN             2
+
+#define LAST_LUN_MODE          2
+
+/* Barossa package */
+#define QFN            0
+#define LQFP           1
+
+/******************/
+/* sd_ctl bit map */
+/******************/
+/* SD push point control, bit 0, 1 */
+#define SD_PUSH_POINT_CTL_MASK         0x03
+#define SD_PUSH_POINT_DELAY            0x01
+#define SD_PUSH_POINT_AUTO             0x02
+/* SD sample point control, bit 2, 3 */
+#define SD_SAMPLE_POINT_CTL_MASK       0x0C
+#define SD_SAMPLE_POINT_DELAY          0x04
+#define SD_SAMPLE_POINT_AUTO           0x08
+/* SD DDR Tx phase set by user, bit 4 */
+#define SD_DDR_TX_PHASE_SET_BY_USER    0x10
+/* MMC DDR Tx phase set by user, bit 5 */
+#define MMC_DDR_TX_PHASE_SET_BY_USER   0x20
+/* Support MMC DDR mode, bit 6 */
+#define SUPPORT_MMC_DDR_MODE           0x40
+/* Reset MMC at first */
+#define RESET_MMC_FIRST                        0x80
+
+#define SEQ_START_CRITERIA             0x20
+
+/* MS Power Class En */
+#define POWER_CLASS_2_EN               0x02
+#define POWER_CLASS_1_EN               0x01
+
+#define MAX_SHOW_CNT                   10
+#define MAX_RESET_CNT                  3
+
+#define SDIO_EXIST                     0x01
+#define SDIO_IGNORED                   0x02
+
+#define CHK_SDIO_EXIST(chip)           ((chip)->sdio_func_exist & SDIO_EXIST)
+#define SET_SDIO_EXIST(chip)           ((chip)->sdio_func_exist |= SDIO_EXIST)
+#define CLR_SDIO_EXIST(chip)           ((chip)->sdio_func_exist &= ~SDIO_EXIST)
+
+#define CHK_SDIO_IGNORED(chip)         ((chip)->sdio_func_exist & SDIO_IGNORED)
+#define SET_SDIO_IGNORED(chip)         ((chip)->sdio_func_exist |= SDIO_IGNORED)
+#define CLR_SDIO_IGNORED(chip)         ((chip)->sdio_func_exist &= ~SDIO_IGNORED)
+
+struct rtsx_chip {
+       rtsx_dev_t      *rtsx;
+
+       u32             int_reg; /* Bus interrupt pending register */
+       char            max_lun;
+       void            *context;
+
+       void            *host_cmds_ptr; /* host commands buffer pointer */
+       dma_addr_t      host_cmds_addr;
+       int             ci;                     /* Command Index */
+
+       void            *host_sg_tbl_ptr;       /* SG descriptor table */
+       dma_addr_t      host_sg_tbl_addr;
+       int             sgi;                    /* SG entry index */
+
+       struct scsi_cmnd        *srb;                   /* current srb */
+       struct sense_data_t     sense_buffer[MAX_ALLOWED_LUN_CNT];
+
+       int                     cur_clk;                /* current card clock */
+
+       /* Current accessed card */
+       int                     cur_card;
+
+       unsigned long   need_release;           /* need release bit map */
+       unsigned long   need_reset;             /* need reset
+                                                * bit map */
+       /* Flag to indicate that this card is just resumed from SS state,
+        * and need released before being resetted
+        */
+       unsigned long           need_reinit;
+
+       int                     rw_need_retry;
+
+#ifdef SUPPORT_OCP
+       u32                     ocp_int;
+       u8                      ocp_stat;
+#endif
+
+       u8      card_exist;     /* card exist bit map (physical exist) */
+       u8      card_ready;     /* card ready bit map (reset successfully) */
+       u8      card_fail;      /* card reset fail bit map */
+       u8      card_ejected;   /* card ejected bit map */
+       u8      card_wp;        /* card write protected bit map */
+
+       u8      lun_mc;         /* flag to indicate whether to answer
+                                * MediaChange */
+
+#ifndef LED_AUTO_BLINK
+       int                     led_toggle_counter;
+#endif
+
+       int                     sd_reset_counter;
+       int                     xd_reset_counter;
+       int                     ms_reset_counter;
+
+       /* card bus width */
+       u8                      card_bus_width[MAX_ALLOWED_LUN_CNT];
+       /* card capacity */
+       u32                     capacity[MAX_ALLOWED_LUN_CNT];
+       /* read/write card function pointer */
+       card_rw_func            rw_card[MAX_ALLOWED_LUN_CNT];
+       /* read/write capacity, used for GPIO Toggle */
+       u32                     rw_cap[MAX_ALLOWED_LUN_CNT];
+       /* card to lun mapping table */
+       u8                      card2lun[32];
+       /* lun to card mapping table */
+       u8                      lun2card[MAX_ALLOWED_LUN_CNT];
+
+       int                     rw_fail_cnt[MAX_ALLOWED_LUN_CNT];
+
+       int                     sd_show_cnt;
+       int                     xd_show_cnt;
+       int                     ms_show_cnt;
+
+       /* card information */
+       struct sd_info          sd_card;
+       struct xd_info          xd_card;
+       struct ms_info          ms_card;
+
+       struct spi_info         spi;
+
+#ifdef _MSG_TRACE
+       struct trace_msg_t      trace_msg[TRACE_ITEM_CNT];
+       int                     msg_idx;
+#endif
+
+       int                     auto_delink_cnt;
+       int                     auto_delink_allowed;
+
+       int                     aspm_enabled;
+
+       int                     sdio_aspm;
+       int                     sdio_idle;
+       int                     sdio_counter;
+       u8                      sdio_raw_data[12];
+
+       u8                      sd_io;
+       u8                      sd_int;
+
+       u8                      rtsx_flag;
+
+       int                     ss_counter;
+       int                     idle_counter;
+       enum RTSX_STAT          rtsx_stat;
+
+       u16                     vendor_id;
+       u16                     product_id;
+       u8                      ic_version;
+
+       int                     driver_first_load;
+
+#ifdef HW_AUTO_SWITCH_SD_BUS
+       int                     sdio_in_charge;
+#endif
+
+       u8                      aspm_level[2];
+
+       int                     chip_insert_with_sdio;
+
+       /* Options */
+
+       int adma_mode;
+
+       int auto_delink_en;
+       int ss_en;
+       u8 lun_mode;
+       u8 aspm_l0s_l1_en;
+
+       int power_down_in_ss;
+
+       int sdr104_en;
+       int ddr50_en;
+       int sdr50_en;
+
+       int baro_pkg;
+
+       int asic_code;
+       int phy_debug_mode;
+       int hw_bypass_sd;
+       int sdio_func_exist;
+       int aux_pwr_exist;
+       u8 ms_power_class_en;
+
+       int mspro_formatter_enable;
+
+       int remote_wakeup_en;
+
+       int ignore_sd;
+       int use_hw_setting;
+
+       int ss_idle_period;
+
+       int dynamic_aspm;
+
+       int fpga_sd_sdr104_clk;
+       int fpga_sd_ddr50_clk;
+       int fpga_sd_sdr50_clk;
+       int fpga_sd_hs_clk;
+       int fpga_mmc_52m_clk;
+       int fpga_ms_hg_clk;
+       int fpga_ms_4bit_clk;
+       int fpga_ms_1bit_clk;
+
+       int asic_sd_sdr104_clk;
+       int asic_sd_ddr50_clk;
+       int asic_sd_sdr50_clk;
+       int asic_sd_hs_clk;
+       int asic_mmc_52m_clk;
+       int asic_ms_hg_clk;
+       int asic_ms_4bit_clk;
+       int asic_ms_1bit_clk;
+
+       u8 ssc_depth_sd_sdr104;
+       u8 ssc_depth_sd_ddr50;
+       u8 ssc_depth_sd_sdr50;
+       u8 ssc_depth_sd_hs;
+       u8 ssc_depth_mmc_52m;
+       u8 ssc_depth_ms_hg;
+       u8 ssc_depth_ms_4bit;
+       u8 ssc_depth_low_speed;
+
+       u8 card_drive_sel;
+       u8 sd30_drive_sel_1v8;
+       u8 sd30_drive_sel_3v3;
+
+       u8 sd_400mA_ocp_thd;
+       u8 sd_800mA_ocp_thd;
+       u8 ms_ocp_thd;
+
+       int ssc_en;
+       int msi_en;
+
+       int xd_timeout;
+       int sd_timeout;
+       int ms_timeout;
+       int mspro_timeout;
+
+       int auto_power_down;
+
+       int sd_ddr_tx_phase;
+       int mmc_ddr_tx_phase;
+       int sd_default_tx_phase;
+       int sd_default_rx_phase;
+
+       int pmos_pwr_on_interval;
+       int sd_voltage_switch_delay;
+       int s3_pwr_off_delay;
+
+       int force_clkreq_0;
+       int ft2_fast_mode;
+
+       int do_delink_before_power_down;
+       int polling_config;
+       int sdio_retry_cnt;
+
+       int delink_stage1_step;
+       int delink_stage2_step;
+       int delink_stage3_step;
+
+       int auto_delink_in_L1;
+       int hp_watch_bios_hotplug;
+       int support_ms_8bit;
+
+       u8 blink_led;
+       u8 phy_voltage;
+       u8 max_payload;
+
+       u32 sd_speed_prior;
+       u32 sd_current_prior;
+       u32 sd_ctl;
+};
+
+#define rtsx_set_stat(chip, stat)                              \
+do {                                                           \
+       if ((stat) != RTSX_STAT_IDLE) {                         \
+               (chip)->idle_counter = 0;                       \
+       }                                                       \
+       (chip)->rtsx_stat = (enum RTSX_STAT)(stat);             \
+} while (0)
+#define rtsx_get_stat(chip)            ((chip)->rtsx_stat)
+#define rtsx_chk_stat(chip, stat)      ((chip)->rtsx_stat == (stat))
+
+#define RTSX_SET_DELINK(chip)  ((chip)->rtsx_flag |= 0x01)
+#define RTSX_CLR_DELINK(chip)  ((chip)->rtsx_flag &= 0xFE)
+#define RTSX_TST_DELINK(chip)  ((chip)->rtsx_flag & 0x01)
+
+#define CHECK_PID(chip, pid)           ((chip)->product_id == (pid))
+#define CHECK_BARO_PKG(chip, pkg)      ((chip)->baro_pkg == (pkg))
+#define CHECK_LUN_MODE(chip, mode)     ((chip)->lun_mode == (mode))
+
+/* Power down control */
+#define SSC_PDCTL              0x01
+#define OC_PDCTL               0x02
+
+int rtsx_force_power_on(struct rtsx_chip *chip, u8 ctl);
+int rtsx_force_power_down(struct rtsx_chip *chip, u8 ctl);
+
+void rtsx_disable_card_int(struct rtsx_chip *chip);
+void rtsx_enable_card_int(struct rtsx_chip *chip);
+void rtsx_enable_bus_int(struct rtsx_chip *chip);
+void rtsx_disable_bus_int(struct rtsx_chip *chip);
+int rtsx_reset_chip(struct rtsx_chip *chip);
+int rtsx_init_chip(struct rtsx_chip *chip);
+void rtsx_release_chip(struct rtsx_chip *chip);
+void rtsx_polling_func(struct rtsx_chip *chip);
+void rtsx_undo_delink(struct rtsx_chip *chip);
+void rtsx_stop_cmd(struct rtsx_chip *chip, int card);
+int rtsx_write_register(struct rtsx_chip *chip, u16 addr, u8 mask, u8 data);
+int rtsx_read_register(struct rtsx_chip *chip, u16 addr, u8 *data);
+int rtsx_write_cfg_dw(struct rtsx_chip *chip,
+               u8 func_no, u16 addr, u32 mask, u32 val);
+int rtsx_read_cfg_dw(struct rtsx_chip *chip, u8 func_no, u16 addr, u32 *val);
+int rtsx_write_cfg_seq(struct rtsx_chip *chip,
+               u8 func, u16 addr, u8 *buf, int len);
+int rtsx_read_cfg_seq(struct rtsx_chip *chip,
+               u8 func, u16 addr, u8 *buf, int len);
+int rtsx_write_phy_register(struct rtsx_chip *chip, u8 addr, u16 val);
+int rtsx_read_phy_register(struct rtsx_chip *chip, u8 addr, u16 *val);
+int rtsx_read_efuse(struct rtsx_chip *chip, u8 addr, u8 *val);
+int rtsx_write_efuse(struct rtsx_chip *chip, u8 addr, u8 val);
+int rtsx_clr_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit);
+int rtsx_set_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit);
+int rtsx_check_link_ready(struct rtsx_chip *chip);
+void rtsx_enter_ss(struct rtsx_chip *chip);
+void rtsx_exit_ss(struct rtsx_chip *chip);
+int rtsx_pre_handle_interrupt(struct rtsx_chip *chip);
+void rtsx_enter_L1(struct rtsx_chip *chip);
+void rtsx_exit_L1(struct rtsx_chip *chip);
+void rtsx_do_before_power_down(struct rtsx_chip *chip, int pm_stat);
+void rtsx_enable_aspm(struct rtsx_chip *chip);
+void rtsx_disable_aspm(struct rtsx_chip *chip);
+int rtsx_read_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len);
+int rtsx_write_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len);
+int rtsx_check_chip_exist(struct rtsx_chip *chip);
+
+#define RTSX_WRITE_REG(chip, addr, mask, data)                         \
+       do {                                                            \
+               int retval = rtsx_write_register((chip), (addr), (mask), (data)); \
+               if (retval != STATUS_SUCCESS) {                         \
+                       TRACE_RET((chip), retval);                      \
+               }                                                       \
+       } while (0)
+
+#define RTSX_READ_REG(chip, addr, data)                                        \
+       do {                                                            \
+               int retval = rtsx_read_register((chip), (addr), (data)); \
+               if (retval != STATUS_SUCCESS) {                         \
+                       TRACE_RET((chip), retval);                      \
+               }                                                       \
+       } while (0)
+
+#endif  /* __REALTEK_RTSX_CHIP_H */
diff --git a/drivers/staging/rts5208/rtsx_scsi.c b/drivers/staging/rts5208/rtsx_scsi.c
new file mode 100644 (file)
index 0000000..382e73a
--- /dev/null
@@ -0,0 +1,3370 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#include "rtsx.h"
+#include "rtsx_transport.h"
+#include "rtsx_sys.h"
+#include "rtsx_card.h"
+#include "rtsx_chip.h"
+#include "rtsx_scsi.h"
+#include "sd.h"
+#include "ms.h"
+#include "spi.h"
+
+void scsi_show_command(struct scsi_cmnd *srb)
+{
+       char *what = NULL;
+       int i, unknown_cmd = 0;
+
+       switch (srb->cmnd[0]) {
+       case TEST_UNIT_READY:
+               what = "TEST_UNIT_READY";
+               break;
+       case REZERO_UNIT:
+               what = "REZERO_UNIT";
+               break;
+       case REQUEST_SENSE:
+               what = "REQUEST_SENSE";
+               break;
+       case FORMAT_UNIT:
+               what = "FORMAT_UNIT";
+               break;
+       case READ_BLOCK_LIMITS:
+               what = "READ_BLOCK_LIMITS";
+               break;
+       case REASSIGN_BLOCKS:
+               what = "REASSIGN_BLOCKS";
+               break;
+       case READ_6:
+               what = "READ_6";
+               break;
+       case WRITE_6:
+               what = "WRITE_6";
+               break;
+       case SEEK_6:
+               what = "SEEK_6";
+               break;
+       case READ_REVERSE:
+               what = "READ_REVERSE";
+               break;
+       case WRITE_FILEMARKS:
+               what = "WRITE_FILEMARKS";
+               break;
+       case SPACE:
+               what = "SPACE";
+               break;
+       case INQUIRY:
+               what = "INQUIRY";
+               break;
+       case RECOVER_BUFFERED_DATA:
+               what = "RECOVER_BUFFERED_DATA";
+               break;
+       case MODE_SELECT:
+               what = "MODE_SELECT";
+               break;
+       case RESERVE:
+               what = "RESERVE";
+               break;
+       case RELEASE:
+               what = "RELEASE";
+               break;
+       case COPY:
+               what = "COPY";
+               break;
+       case ERASE:
+               what = "ERASE";
+               break;
+       case MODE_SENSE:
+               what = "MODE_SENSE";
+               break;
+       case START_STOP:
+               what = "START_STOP";
+               break;
+       case RECEIVE_DIAGNOSTIC:
+               what = "RECEIVE_DIAGNOSTIC";
+               break;
+       case SEND_DIAGNOSTIC:
+               what = "SEND_DIAGNOSTIC";
+               break;
+       case ALLOW_MEDIUM_REMOVAL:
+               what = "ALLOW_MEDIUM_REMOVAL";
+               break;
+       case SET_WINDOW:
+               what = "SET_WINDOW";
+               break;
+       case READ_CAPACITY:
+               what = "READ_CAPACITY";
+               break;
+       case READ_10:
+               what = "READ_10";
+               break;
+       case WRITE_10:
+               what = "WRITE_10";
+               break;
+       case SEEK_10:
+               what = "SEEK_10";
+               break;
+       case WRITE_VERIFY:
+               what = "WRITE_VERIFY";
+               break;
+       case VERIFY:
+               what = "VERIFY";
+               break;
+       case SEARCH_HIGH:
+               what = "SEARCH_HIGH";
+               break;
+       case SEARCH_EQUAL:
+               what = "SEARCH_EQUAL";
+               break;
+       case SEARCH_LOW:
+               what = "SEARCH_LOW";
+               break;
+       case SET_LIMITS:
+               what = "SET_LIMITS";
+               break;
+       case READ_POSITION:
+               what = "READ_POSITION";
+               break;
+       case SYNCHRONIZE_CACHE:
+               what = "SYNCHRONIZE_CACHE";
+               break;
+       case LOCK_UNLOCK_CACHE:
+               what = "LOCK_UNLOCK_CACHE";
+               break;
+       case READ_DEFECT_DATA:
+               what = "READ_DEFECT_DATA";
+               break;
+       case MEDIUM_SCAN:
+               what = "MEDIUM_SCAN";
+               break;
+       case COMPARE:
+               what = "COMPARE";
+               break;
+       case COPY_VERIFY:
+               what = "COPY_VERIFY";
+               break;
+       case WRITE_BUFFER:
+               what = "WRITE_BUFFER";
+               break;
+       case READ_BUFFER:
+               what = "READ_BUFFER";
+               break;
+       case UPDATE_BLOCK:
+               what = "UPDATE_BLOCK";
+               break;
+       case READ_LONG:
+               what = "READ_LONG";
+               break;
+       case WRITE_LONG:
+               what = "WRITE_LONG";
+               break;
+       case CHANGE_DEFINITION:
+               what = "CHANGE_DEFINITION";
+               break;
+       case WRITE_SAME:
+               what = "WRITE_SAME";
+               break;
+       case GPCMD_READ_SUBCHANNEL:
+               what = "READ SUBCHANNEL";
+               break;
+       case READ_TOC:
+               what = "READ_TOC";
+               break;
+       case GPCMD_READ_HEADER:
+               what = "READ HEADER";
+               break;
+       case GPCMD_PLAY_AUDIO_10:
+               what = "PLAY AUDIO (10)";
+               break;
+       case GPCMD_PLAY_AUDIO_MSF:
+               what = "PLAY AUDIO MSF";
+               break;
+       case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
+               what = "GET EVENT/STATUS NOTIFICATION";
+               break;
+       case GPCMD_PAUSE_RESUME:
+               what = "PAUSE/RESUME";
+               break;
+       case LOG_SELECT:
+               what = "LOG_SELECT";
+               break;
+       case LOG_SENSE:
+               what = "LOG_SENSE";
+               break;
+       case GPCMD_STOP_PLAY_SCAN:
+               what = "STOP PLAY/SCAN";
+               break;
+       case GPCMD_READ_DISC_INFO:
+               what = "READ DISC INFORMATION";
+               break;
+       case GPCMD_READ_TRACK_RZONE_INFO:
+               what = "READ TRACK INFORMATION";
+               break;
+       case GPCMD_RESERVE_RZONE_TRACK:
+               what = "RESERVE TRACK";
+               break;
+       case GPCMD_SEND_OPC:
+               what = "SEND OPC";
+               break;
+       case MODE_SELECT_10:
+               what = "MODE_SELECT_10";
+               break;
+       case GPCMD_REPAIR_RZONE_TRACK:
+               what = "REPAIR TRACK";
+               break;
+       case 0x59:
+               what = "READ MASTER CUE";
+               break;
+       case MODE_SENSE_10:
+               what = "MODE_SENSE_10";
+               break;
+       case GPCMD_CLOSE_TRACK:
+               what = "CLOSE TRACK/SESSION";
+               break;
+       case 0x5C:
+               what = "READ BUFFER CAPACITY";
+               break;
+       case 0x5D:
+               what = "SEND CUE SHEET";
+               break;
+       case GPCMD_BLANK:
+               what = "BLANK";
+               break;
+       case REPORT_LUNS:
+               what = "REPORT LUNS";
+               break;
+       case MOVE_MEDIUM:
+               what = "MOVE_MEDIUM or PLAY AUDIO (12)";
+               break;
+       case READ_12:
+               what = "READ_12";
+               break;
+       case WRITE_12:
+               what = "WRITE_12";
+               break;
+       case WRITE_VERIFY_12:
+               what = "WRITE_VERIFY_12";
+               break;
+       case SEARCH_HIGH_12:
+               what = "SEARCH_HIGH_12";
+               break;
+       case SEARCH_EQUAL_12:
+               what = "SEARCH_EQUAL_12";
+               break;
+       case SEARCH_LOW_12:
+               what = "SEARCH_LOW_12";
+               break;
+       case SEND_VOLUME_TAG:
+               what = "SEND_VOLUME_TAG";
+               break;
+       case READ_ELEMENT_STATUS:
+               what = "READ_ELEMENT_STATUS";
+               break;
+       case GPCMD_READ_CD_MSF:
+               what = "READ CD MSF";
+               break;
+       case GPCMD_SCAN:
+               what = "SCAN";
+               break;
+       case GPCMD_SET_SPEED:
+               what = "SET CD SPEED";
+               break;
+       case GPCMD_MECHANISM_STATUS:
+               what = "MECHANISM STATUS";
+               break;
+       case GPCMD_READ_CD:
+               what = "READ CD";
+               break;
+       case 0xE1:
+               what = "WRITE CONTINUE";
+               break;
+       case WRITE_LONG_2:
+               what = "WRITE_LONG_2";
+               break;
+       case VENDOR_CMND:
+               what = "Realtek's vendor command";
+               break;
+       default:
+               what = "(unknown command)"; unknown_cmd = 1;
+               break;
+       }
+
+       if (srb->cmnd[0] != TEST_UNIT_READY)
+               RTSX_DEBUGP("Command %s (%d bytes)\n", what, srb->cmd_len);
+
+       if (unknown_cmd) {
+               RTSX_DEBUGP("");
+               for (i = 0; i < srb->cmd_len && i < 16; i++)
+                       RTSX_DEBUGPN(" %02x", srb->cmnd[i]);
+               RTSX_DEBUGPN("\n");
+       }
+}
+
+void set_sense_type(struct rtsx_chip *chip, unsigned int lun, int sense_type)
+{
+       switch (sense_type) {
+       case SENSE_TYPE_MEDIA_CHANGE:
+               set_sense_data(chip, lun, CUR_ERR, 0x06, 0, 0x28, 0, 0, 0);
+               break;
+
+       case SENSE_TYPE_MEDIA_NOT_PRESENT:
+               set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x3A, 0, 0, 0);
+               break;
+
+       case SENSE_TYPE_MEDIA_LBA_OVER_RANGE:
+               set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x21, 0, 0, 0);
+               break;
+
+       case SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT:
+               set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x25, 0, 0, 0);
+               break;
+
+       case SENSE_TYPE_MEDIA_WRITE_PROTECT:
+               set_sense_data(chip, lun, CUR_ERR, 0x07, 0, 0x27, 0, 0, 0);
+               break;
+
+       case SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR:
+               set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x11, 0, 0, 0);
+               break;
+
+       case SENSE_TYPE_MEDIA_WRITE_ERR:
+               set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x0C, 0x02, 0, 0);
+               break;
+
+       case SENSE_TYPE_MEDIA_INVALID_CMD_FIELD:
+               set_sense_data(chip, lun, CUR_ERR, ILGAL_REQ, 0,
+                               ASC_INVLD_CDB, ASCQ_INVLD_CDB, CDB_ILLEGAL, 1);
+               break;
+
+       case SENSE_TYPE_FORMAT_IN_PROGRESS:
+               set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04, 0, 0);
+               break;
+
+       case SENSE_TYPE_FORMAT_CMD_FAILED:
+               set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x31, 0x01, 0, 0);
+               break;
+
+#ifdef SUPPORT_MAGIC_GATE
+       case SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB:
+               set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x6F, 0x02, 0, 0);
+               break;
+
+       case SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN:
+               set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x6F, 0x00, 0, 0);
+               break;
+
+       case SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM:
+               set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x30, 0x00, 0, 0);
+               break;
+
+       case SENSE_TYPE_MG_WRITE_ERR:
+               set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x0C, 0x00, 0, 0);
+               break;
+#endif
+
+#ifdef SUPPORT_SD_LOCK
+       case SENSE_TYPE_MEDIA_READ_FORBIDDEN:
+               set_sense_data(chip, lun, CUR_ERR, 0x07, 0, 0x11, 0x13, 0, 0);
+               break;
+#endif
+
+       case SENSE_TYPE_NO_SENSE:
+       default:
+               set_sense_data(chip, lun, CUR_ERR, 0, 0, 0, 0, 0, 0);
+               break;
+       }
+}
+
+void set_sense_data(struct rtsx_chip *chip, unsigned int lun, u8 err_code,
+               u8 sense_key, u32 info, u8 asc, u8 ascq, u8 sns_key_info0,
+               u16 sns_key_info1)
+{
+       struct sense_data_t *sense = &(chip->sense_buffer[lun]);
+
+       sense->err_code = err_code;
+       sense->sense_key = sense_key;
+       sense->info[0] = (u8)(info >> 24);
+       sense->info[1] = (u8)(info >> 16);
+       sense->info[2] = (u8)(info >> 8);
+       sense->info[3] = (u8)info;
+
+       sense->ad_sense_len = sizeof(struct sense_data_t) - 8;
+       sense->asc = asc;
+       sense->ascq = ascq;
+       if (sns_key_info0 != 0) {
+               sense->sns_key_info[0] = SKSV | sns_key_info0;
+               sense->sns_key_info[1] = (sns_key_info1 & 0xf0) >> 8;
+               sense->sns_key_info[2] = sns_key_info1 & 0x0f;
+       }
+}
+
+static int test_unit_ready(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned int lun = SCSI_LUN(srb);
+
+       if (!check_card_ready(chip, lun)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               return TRANSPORT_FAILED;
+       }
+
+       if (!(CHK_BIT(chip->lun_mc, lun))) {
+               SET_BIT(chip->lun_mc, lun);
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+               return TRANSPORT_FAILED;
+       }
+
+#ifdef SUPPORT_SD_LOCK
+       if (get_lun_card(chip, SCSI_LUN(srb)) == SD_CARD) {
+               struct sd_info *sd_card = &(chip->sd_card);
+               if (sd_card->sd_lock_notify) {
+                       sd_card->sd_lock_notify = 0;
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+                       return TRANSPORT_FAILED;
+               } else if (sd_card->sd_lock_status & SD_LOCKED) {
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_READ_FORBIDDEN);
+                       return TRANSPORT_FAILED;
+               }
+       }
+#endif
+
+       return TRANSPORT_GOOD;
+}
+
+static unsigned char formatter_inquiry_str[20] = {
+       'M', 'E', 'M', 'O', 'R', 'Y', 'S', 'T', 'I', 'C', 'K',
+#ifdef SUPPORT_MAGIC_GATE
+       '-', 'M', 'G', /* Byte[47:49] */
+#else
+       0x20, 0x20, 0x20,  /* Byte[47:49] */
+#endif
+
+#ifdef SUPPORT_MAGIC_GATE
+       0x0B,  /* Byte[50]: MG, MS, MSPro, MSXC */
+#else
+       0x09,  /* Byte[50]: MS, MSPro, MSXC */
+#endif
+       0x00,  /* Byte[51]: Category Specific Commands */
+       0x00,  /* Byte[52]: Access Control and feature */
+       0x20, 0x20, 0x20, /* Byte[53:55] */
+};
+
+static int inquiry(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned int lun = SCSI_LUN(srb);
+       char *inquiry_default = (char *)"Generic-xD/SD/M.S.      1.00 ";
+       char *inquiry_sdms =    (char *)"Generic-SD/MemoryStick  1.00 ";
+       char *inquiry_sd =      (char *)"Generic-SD/MMC          1.00 ";
+       char *inquiry_ms =      (char *)"Generic-MemoryStick     1.00 ";
+       char *inquiry_string;
+       unsigned char sendbytes;
+       unsigned char *buf;
+       u8 card = get_lun_card(chip, lun);
+       int pro_formatter_flag = 0;
+       unsigned char inquiry_buf[] = {
+               QULIFIRE|DRCT_ACCESS_DEV,
+               RMB_DISC|0x0D,
+               0x00,
+               0x01,
+               0x1f,
+               0x02,
+               0,
+               REL_ADR|WBUS_32|WBUS_16|SYNC|LINKED|CMD_QUE|SFT_RE,
+       };
+
+       if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+               if (chip->lun2card[lun] == SD_CARD)
+                       inquiry_string = inquiry_sd;
+               else
+                       inquiry_string = inquiry_ms;
+
+       } else if (CHECK_LUN_MODE(chip, SD_MS_1LUN)) {
+               inquiry_string = inquiry_sdms;
+       } else {
+               inquiry_string = inquiry_default;
+       }
+
+       buf = vmalloc(scsi_bufflen(srb));
+       if (buf == NULL)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+#ifdef SUPPORT_MAGIC_GATE
+       if ((chip->mspro_formatter_enable) &&
+                       (chip->lun2card[lun] & MS_CARD))
+#else
+       if (chip->mspro_formatter_enable)
+#endif
+       {
+               if (!card || (card == MS_CARD))
+                       pro_formatter_flag = 1;
+       }
+
+       if (pro_formatter_flag) {
+               if (scsi_bufflen(srb) < 56)
+                       sendbytes = (unsigned char)(scsi_bufflen(srb));
+               else
+                       sendbytes = 56;
+
+       } else {
+               if (scsi_bufflen(srb) < 36)
+                       sendbytes = (unsigned char)(scsi_bufflen(srb));
+               else
+                       sendbytes = 36;
+       }
+
+       if (sendbytes > 8) {
+               memcpy(buf, inquiry_buf, 8);
+               memcpy(buf + 8, inquiry_string, sendbytes - 8);
+               if (pro_formatter_flag) {
+                       /* Additional Length */
+                       buf[4] = 0x33;
+               }
+       } else {
+               memcpy(buf, inquiry_buf, sendbytes);
+       }
+
+       if (pro_formatter_flag) {
+               if (sendbytes > 36)
+                       memcpy(buf + 36, formatter_inquiry_str, sendbytes - 36);
+       }
+
+       scsi_set_resid(srb, 0);
+
+       rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
+       vfree(buf);
+
+       return TRANSPORT_GOOD;
+}
+
+
+static int start_stop_unit(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned int lun = SCSI_LUN(srb);
+
+       scsi_set_resid(srb, scsi_bufflen(srb));
+
+       if (srb->cmnd[1] == 1)
+               return TRANSPORT_GOOD;
+
+       switch (srb->cmnd[0x4]) {
+       case STOP_MEDIUM:
+               /* Media disabled */
+               return TRANSPORT_GOOD;
+
+       case UNLOAD_MEDIUM:
+               /* Media shall be unload */
+               if (check_card_ready(chip, lun))
+                       eject_card(chip, lun);
+               return TRANSPORT_GOOD;
+
+       case MAKE_MEDIUM_READY:
+       case LOAD_MEDIUM:
+               if (check_card_ready(chip, lun)) {
+                       return TRANSPORT_GOOD;
+               } else {
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+
+               break;
+       }
+
+       TRACE_RET(chip, TRANSPORT_ERROR);
+}
+
+
+static int allow_medium_removal(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int prevent;
+
+       prevent = srb->cmnd[4] & 0x1;
+
+       scsi_set_resid(srb, 0);
+
+       if (prevent) {
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       return TRANSPORT_GOOD;
+}
+
+
+static int request_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct sense_data_t *sense;
+       unsigned int lun = SCSI_LUN(srb);
+       struct ms_info *ms_card = &(chip->ms_card);
+       unsigned char *tmp, *buf;
+
+       sense = &(chip->sense_buffer[lun]);
+
+       if ((get_lun_card(chip, lun) == MS_CARD) &&
+               ms_card->pro_under_formatting) {
+               if (ms_card->format_status == FORMAT_SUCCESS) {
+                       set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
+                       ms_card->pro_under_formatting = 0;
+                       ms_card->progress = 0;
+               } else if (ms_card->format_status == FORMAT_IN_PROGRESS) {
+                       /* Logical Unit Not Ready Format in Progress */
+                       set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04,
+                                       0, (u16)(ms_card->progress));
+               } else {
+                       /* Format Command Failed */
+                       set_sense_type(chip, lun, SENSE_TYPE_FORMAT_CMD_FAILED);
+                       ms_card->pro_under_formatting = 0;
+                       ms_card->progress = 0;
+               }
+
+               rtsx_set_stat(chip, RTSX_STAT_RUN);
+       }
+
+       buf = vmalloc(scsi_bufflen(srb));
+       if (buf == NULL)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       tmp = (unsigned char *)sense;
+       memcpy(buf, tmp, scsi_bufflen(srb));
+
+       rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
+       vfree(buf);
+
+       scsi_set_resid(srb, 0);
+       /* Reset Sense Data */
+       set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
+       return TRANSPORT_GOOD;
+}
+
+static void ms_mode_sense(struct rtsx_chip *chip, u8 cmd,
+               int lun, u8 *buf, int buf_len)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       int sys_info_offset;
+       int data_size = buf_len;
+       int support_format = 0;
+       int i = 0;
+
+       if (cmd == MODE_SENSE) {
+               sys_info_offset = 8;
+               if (data_size > 0x68)
+                       data_size = 0x68;
+
+               buf[i++] = 0x67;  /* Mode Data Length */
+       } else {
+               sys_info_offset = 12;
+               if (data_size > 0x6C)
+                       data_size = 0x6C;
+
+               buf[i++] = 0x00;  /* Mode Data Length (MSB) */
+               buf[i++] = 0x6A;  /* Mode Data Length (LSB) */
+       }
+
+       /* Medium Type Code */
+       if (check_card_ready(chip, lun)) {
+               if (CHK_MSXC(ms_card)) {
+                       support_format = 1;
+                       buf[i++] = 0x40;
+               } else if (CHK_MSPRO(ms_card)) {
+                       support_format = 1;
+                       buf[i++] = 0x20;
+               } else {
+                       buf[i++] = 0x10;
+               }
+
+               /* WP */
+               if (check_card_wp(chip, lun))
+                       buf[i++] = 0x80;
+               else
+                       buf[i++] = 0x00;
+
+       } else {
+               buf[i++] = 0x00;        /* MediaType */
+               buf[i++] = 0x00;        /* WP */
+       }
+
+       buf[i++] = 0x00;                /* Reserved */
+
+       if (cmd == MODE_SENSE_10) {
+               buf[i++] = 0x00;  /* Reserved */
+               buf[i++] = 0x00;  /* Block descriptor length(MSB) */
+               buf[i++] = 0x00;  /* Block descriptor length(LSB) */
+
+               /* The Following Data is the content of "Page 0x20" */
+               if (data_size >= 9)
+                       buf[i++] = 0x20;                /* Page Code */
+               if (data_size >= 10)
+                       buf[i++] = 0x62;                /* Page Length */
+               if (data_size >= 11)
+                       buf[i++] = 0x00;                /* No Access Control */
+               if (data_size >= 12) {
+                       if (support_format)
+                               buf[i++] = 0xC0;        /* SF, SGM */
+                       else
+                               buf[i++] = 0x00;
+               }
+       } else {
+               /* The Following Data is the content of "Page 0x20" */
+               if (data_size >= 5)
+                       buf[i++] = 0x20;                /* Page Code */
+               if (data_size >= 6)
+                       buf[i++] = 0x62;                /* Page Length */
+               if (data_size >= 7)
+                       buf[i++] = 0x00;                /* No Access Control */
+               if (data_size >= 8) {
+                       if (support_format)
+                               buf[i++] = 0xC0;        /* SF, SGM */
+                       else
+                               buf[i++] = 0x00;
+               }
+       }
+
+       if (data_size > sys_info_offset) {
+               /* 96 Bytes Attribute Data */
+               int len = data_size - sys_info_offset;
+               len = (len < 96) ? len : 96;
+
+               memcpy(buf + sys_info_offset, ms_card->raw_sys_info, len);
+       }
+}
+
+static int mode_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned int lun = SCSI_LUN(srb);
+       unsigned int dataSize;
+       int status;
+       int pro_formatter_flag;
+       unsigned char pageCode, *buf;
+       u8 card = get_lun_card(chip, lun);
+
+#ifndef SUPPORT_MAGIC_GATE
+       if (!check_card_ready(chip, lun)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               scsi_set_resid(srb, scsi_bufflen(srb));
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+#endif
+
+       pro_formatter_flag = 0;
+       dataSize = 8;
+#ifdef SUPPORT_MAGIC_GATE
+       if ((chip->lun2card[lun] & MS_CARD)) {
+               if (!card || (card == MS_CARD)) {
+                       dataSize = 108;
+                       if (chip->mspro_formatter_enable)
+                               pro_formatter_flag = 1;
+               }
+       }
+#else
+       if (card == MS_CARD) {
+               if (chip->mspro_formatter_enable) {
+                       pro_formatter_flag = 1;
+                       dataSize = 108;
+               }
+       }
+#endif
+
+       buf = kmalloc(dataSize, GFP_KERNEL);
+       if (buf == NULL)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       pageCode = srb->cmnd[2] & 0x3f;
+
+       if ((pageCode == 0x3F) || (pageCode == 0x1C) ||
+               (pageCode == 0x00) ||
+               (pro_formatter_flag && (pageCode == 0x20))) {
+               if (srb->cmnd[0] == MODE_SENSE) {
+                       if ((pageCode == 0x3F) || (pageCode == 0x20)) {
+                               ms_mode_sense(chip, srb->cmnd[0],
+                                             lun, buf, dataSize);
+                       } else {
+                               dataSize = 4;
+                               buf[0] = 0x03;
+                               buf[1] = 0x00;
+                               if (check_card_wp(chip, lun))
+                                       buf[2] = 0x80;
+                               else
+                                       buf[2] = 0x00;
+
+                               buf[3] = 0x00;
+                       }
+               } else {
+                       if ((pageCode == 0x3F) || (pageCode == 0x20)) {
+                               ms_mode_sense(chip, srb->cmnd[0],
+                                             lun, buf, dataSize);
+                       } else {
+                               dataSize = 8;
+                               buf[0] = 0x00;
+                               buf[1] = 0x06;
+                               buf[2] = 0x00;
+                               if (check_card_wp(chip, lun))
+                                       buf[3] = 0x80;
+                               else
+                                       buf[3] = 0x00;
+                               buf[4] = 0x00;
+                               buf[5] = 0x00;
+                               buf[6] = 0x00;
+                               buf[7] = 0x00;
+                       }
+               }
+               status = TRANSPORT_GOOD;
+       } else {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               scsi_set_resid(srb, scsi_bufflen(srb));
+               status = TRANSPORT_FAILED;
+       }
+
+       if (status == TRANSPORT_GOOD) {
+               unsigned int len = min_t(unsigned int, scsi_bufflen(srb),
+                                       dataSize);
+               rtsx_stor_set_xfer_buf(buf, len, srb);
+               scsi_set_resid(srb, scsi_bufflen(srb) - len);
+       }
+       kfree(buf);
+
+       return status;
+}
+
+static int read_write(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+#ifdef SUPPORT_SD_LOCK
+       struct sd_info *sd_card = &(chip->sd_card);
+#endif
+       unsigned int lun = SCSI_LUN(srb);
+       int retval;
+       u32 start_sec;
+       u16 sec_cnt;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       if (!check_card_ready(chip, lun) || (get_card_size(chip, lun) == 0)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (!(CHK_BIT(chip->lun_mc, lun))) {
+               SET_BIT(chip->lun_mc, lun);
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+               return TRANSPORT_FAILED;
+       }
+
+#ifdef SUPPORT_SD_LOCK
+       if (sd_card->sd_erase_status) {
+               /* Accessing to any card is forbidden
+                * until the erase procedure of SD is completed
+                */
+               RTSX_DEBUGP("SD card being erased!\n");
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_READ_FORBIDDEN);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (get_lun_card(chip, lun) == SD_CARD) {
+               if (sd_card->sd_lock_status & SD_LOCKED) {
+                       RTSX_DEBUGP("SD card locked!\n");
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_READ_FORBIDDEN);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+#endif
+
+       if ((srb->cmnd[0] == READ_10) || (srb->cmnd[0] == WRITE_10)) {
+               start_sec = ((u32)srb->cmnd[2] << 24) |
+                       ((u32)srb->cmnd[3] << 16) |
+                       ((u32)srb->cmnd[4] << 8) | ((u32)srb->cmnd[5]);
+               sec_cnt = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
+       } else if ((srb->cmnd[0] == READ_6) || (srb->cmnd[0] == WRITE_6)) {
+               start_sec = ((u32)(srb->cmnd[1] & 0x1F) << 16) |
+                       ((u32)srb->cmnd[2] << 8) | ((u32)srb->cmnd[3]);
+               sec_cnt = srb->cmnd[4];
+       } else if ((srb->cmnd[0] == VENDOR_CMND) &&
+               (srb->cmnd[1] == SCSI_APP_CMD) &&
+               ((srb->cmnd[2] == PP_READ10) || (srb->cmnd[2] == PP_WRITE10))) {
+               start_sec = ((u32)srb->cmnd[4] << 24) |
+                       ((u32)srb->cmnd[5] << 16) |
+                       ((u32)srb->cmnd[6] << 8) | ((u32)srb->cmnd[7]);
+               sec_cnt = ((u16)(srb->cmnd[9]) << 8) | srb->cmnd[10];
+       } else {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       /* In some test, we will receive a start_sec like 0xFFFFFFFF.
+        * In this situation, start_sec + sec_cnt will overflow, so we
+        * need to judge start_sec at first
+        */
+       if ((start_sec > get_card_size(chip, lun)) ||
+                       ((start_sec + sec_cnt) > get_card_size(chip, lun))) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LBA_OVER_RANGE);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (sec_cnt == 0) {
+               scsi_set_resid(srb, 0);
+               return TRANSPORT_GOOD;
+       }
+
+       if (chip->rw_fail_cnt[lun] == 3) {
+               RTSX_DEBUGP("read/write fail three times in succession\n");
+               if (srb->sc_data_direction == DMA_FROM_DEVICE)
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+               else
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
+
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (srb->sc_data_direction == DMA_TO_DEVICE) {
+               if (check_card_wp(chip, lun)) {
+                       RTSX_DEBUGP("Write protected card!\n");
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_WRITE_PROTECT);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+
+       retval = card_rw(srb, chip, start_sec, sec_cnt);
+       if (retval != STATUS_SUCCESS) {
+               if (chip->need_release & chip->lun2card[lun]) {
+                       chip->rw_fail_cnt[lun] = 0;
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               } else {
+                       chip->rw_fail_cnt[lun]++;
+                       if (srb->sc_data_direction == DMA_FROM_DEVICE)
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                       else
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+               }
+               retval = TRANSPORT_FAILED;
+               TRACE_GOTO(chip, Exit);
+       } else {
+               chip->rw_fail_cnt[lun] = 0;
+               retval = TRANSPORT_GOOD;
+       }
+
+       scsi_set_resid(srb, 0);
+
+Exit:
+       return retval;
+}
+
+static int read_format_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned char *buf;
+       unsigned int lun = SCSI_LUN(srb);
+       unsigned int buf_len;
+       u8 card = get_lun_card(chip, lun);
+       u32 card_size;
+       int desc_cnt;
+       int i = 0;
+
+       if (!check_card_ready(chip, lun)) {
+               if (!chip->mspro_formatter_enable) {
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+
+       buf_len = (scsi_bufflen(srb) > 12) ? 0x14 : 12;
+
+       buf = kmalloc(buf_len, GFP_KERNEL);
+       if (buf == NULL)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       buf[i++] = 0;
+       buf[i++] = 0;
+       buf[i++] = 0;
+
+       /* Capacity List Length */
+       if ((buf_len > 12) && chip->mspro_formatter_enable &&
+                       (chip->lun2card[lun] & MS_CARD) &&
+                       (!card || (card == MS_CARD))) {
+               buf[i++] = 0x10;
+               desc_cnt = 2;
+       } else {
+               buf[i++] = 0x08;
+               desc_cnt = 1;
+       }
+
+       while (desc_cnt) {
+               if (check_card_ready(chip, lun)) {
+                       card_size = get_card_size(chip, lun);
+                       buf[i++] = (unsigned char)(card_size >> 24);
+                       buf[i++] = (unsigned char)(card_size >> 16);
+                       buf[i++] = (unsigned char)(card_size >> 8);
+                       buf[i++] = (unsigned char)card_size;
+
+                       if (desc_cnt == 2)
+                               buf[i++] = 2;
+                       else
+                               buf[i++] = 0;
+               } else {
+                       buf[i++] = 0xFF;
+                       buf[i++] = 0xFF;
+                       buf[i++] = 0xFF;
+                       buf[i++] = 0xFF;
+
+                       if (desc_cnt == 2)
+                               buf[i++] = 3;
+                       else
+                               buf[i++] = 0;
+               }
+
+               buf[i++] = 0x00;
+               buf[i++] = 0x02;
+               buf[i++] = 0x00;
+
+               desc_cnt--;
+       }
+
+       buf_len = min_t(unsigned int, scsi_bufflen(srb), buf_len);
+       rtsx_stor_set_xfer_buf(buf, buf_len, srb);
+       kfree(buf);
+
+       scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);
+
+       return TRANSPORT_GOOD;
+}
+
+static int read_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned char *buf;
+       unsigned int lun = SCSI_LUN(srb);
+       u32 card_size;
+
+       if (!check_card_ready(chip, lun)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (!(CHK_BIT(chip->lun_mc, lun))) {
+               SET_BIT(chip->lun_mc, lun);
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+               return TRANSPORT_FAILED;
+       }
+
+       buf = kmalloc(8, GFP_KERNEL);
+       if (buf == NULL)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       card_size = get_card_size(chip, lun);
+       buf[0] = (unsigned char)((card_size - 1) >> 24);
+       buf[1] = (unsigned char)((card_size - 1) >> 16);
+       buf[2] = (unsigned char)((card_size - 1) >> 8);
+       buf[3] = (unsigned char)(card_size - 1);
+
+       buf[4] = 0x00;
+       buf[5] = 0x00;
+       buf[6] = 0x02;
+       buf[7] = 0x00;
+
+       rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
+       kfree(buf);
+
+       scsi_set_resid(srb, 0);
+
+       return TRANSPORT_GOOD;
+}
+
+static int read_eeprom(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned short len, i;
+       int retval;
+       u8 *buf;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+
+       buf = vmalloc(len);
+       if (!buf)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       retval = rtsx_force_power_on(chip, SSC_PDCTL);
+       if (retval != STATUS_SUCCESS) {
+               vfree(buf);
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       for (i = 0; i < len; i++) {
+               retval = spi_read_eeprom(chip, i, buf + i);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(buf);
+                       set_sense_type(chip, SCSI_LUN(srb),
+                               SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+
+       len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
+       rtsx_stor_set_xfer_buf(buf, len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+       vfree(buf);
+
+       return TRANSPORT_GOOD;
+}
+
+static int write_eeprom(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned short len, i;
+       int retval;
+       u8 *buf;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+
+       retval = rtsx_force_power_on(chip, SSC_PDCTL);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (len == 511) {
+               retval = spi_erase_eeprom_chip(chip);
+               if (retval != STATUS_SUCCESS) {
+                       set_sense_type(chip, SCSI_LUN(srb),
+                               SENSE_TYPE_MEDIA_WRITE_ERR);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       } else {
+               len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb),
+                                       len);
+               buf = vmalloc(len);
+               if (buf == NULL)
+                       TRACE_RET(chip, TRANSPORT_ERROR);
+
+               rtsx_stor_get_xfer_buf(buf, len, srb);
+               scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+               for (i = 0; i < len; i++) {
+                       retval = spi_write_eeprom(chip, i, buf[i]);
+                       if (retval != STATUS_SUCCESS) {
+                               vfree(buf);
+                               set_sense_type(chip, SCSI_LUN(srb),
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+                       }
+               }
+
+               vfree(buf);
+       }
+
+       return TRANSPORT_GOOD;
+}
+
+static int read_mem(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned short addr, len, i;
+       int retval;
+       u8 *buf;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       addr = ((u16)srb->cmnd[2] << 8) | srb->cmnd[3];
+       len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+
+       if (addr < 0xFC00) {
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       buf = vmalloc(len);
+       if (!buf)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       retval = rtsx_force_power_on(chip, SSC_PDCTL);
+       if (retval != STATUS_SUCCESS) {
+               vfree(buf);
+               set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       for (i = 0; i < len; i++) {
+               retval = rtsx_read_register(chip, addr + i, buf + i);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(buf);
+                       set_sense_type(chip, SCSI_LUN(srb),
+                               SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+
+       len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
+       rtsx_stor_set_xfer_buf(buf, len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+       vfree(buf);
+
+       return TRANSPORT_GOOD;
+}
+
+static int write_mem(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned short addr, len, i;
+       int retval;
+       u8 *buf;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       addr = ((u16)srb->cmnd[2] << 8) | srb->cmnd[3];
+       len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+
+       if (addr < 0xFC00) {
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
+       buf = vmalloc(len);
+       if (buf == NULL)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       rtsx_stor_get_xfer_buf(buf, len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+       retval = rtsx_force_power_on(chip, SSC_PDCTL);
+       if (retval != STATUS_SUCCESS) {
+               vfree(buf);
+               set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       for (i = 0; i < len; i++) {
+               retval = rtsx_write_register(chip, addr + i, 0xFF, buf[i]);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(buf);
+                       set_sense_type(chip, SCSI_LUN(srb),
+                               SENSE_TYPE_MEDIA_WRITE_ERR);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+
+       vfree(buf);
+
+       return TRANSPORT_GOOD;
+}
+
+static int get_sd_csd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       unsigned int lun = SCSI_LUN(srb);
+
+       if (!check_card_ready(chip, lun)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (get_lun_card(chip, lun) != SD_CARD) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       scsi_set_resid(srb, 0);
+       rtsx_stor_set_xfer_buf(sd_card->raw_csd, scsi_bufflen(srb), srb);
+
+       return TRANSPORT_GOOD;
+}
+
+static int toggle_gpio_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       u8 gpio = srb->cmnd[2];
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       if (gpio > 3)
+               gpio = 1;
+       toggle_gpio(chip, gpio);
+
+       return TRANSPORT_GOOD;
+}
+
+#ifdef _MSG_TRACE
+static int trace_msg_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned char *ptr, *buf = NULL;
+       int i, msg_cnt;
+       u8 clear;
+       unsigned int buf_len;
+
+       buf_len = 4 + ((2 + MSG_FUNC_LEN + MSG_FILE_LEN + TIME_VAL_LEN) *
+               TRACE_ITEM_CNT);
+
+       if ((scsi_bufflen(srb) < buf_len) || (scsi_sglist(srb) == NULL)) {
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       clear = srb->cmnd[2];
+
+       buf = vmalloc(scsi_bufflen(srb));
+       if (buf == NULL)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+       ptr = buf;
+
+       if (chip->trace_msg[chip->msg_idx].valid)
+               msg_cnt = TRACE_ITEM_CNT;
+       else
+               msg_cnt = chip->msg_idx;
+
+       *(ptr++) = (u8)(msg_cnt >> 24);
+       *(ptr++) = (u8)(msg_cnt >> 16);
+       *(ptr++) = (u8)(msg_cnt >> 8);
+       *(ptr++) = (u8)msg_cnt;
+       RTSX_DEBUGP("Trace message count is %d\n", msg_cnt);
+
+       for (i = 1; i <= msg_cnt; i++) {
+               int j, idx;
+
+               idx = chip->msg_idx - i;
+               if (idx < 0)
+                       idx += TRACE_ITEM_CNT;
+
+               *(ptr++) = (u8)(chip->trace_msg[idx].line >> 8);
+               *(ptr++) = (u8)(chip->trace_msg[idx].line);
+               for (j = 0; j < MSG_FUNC_LEN; j++)
+                       *(ptr++) = chip->trace_msg[idx].func[j];
+
+               for (j = 0; j < MSG_FILE_LEN; j++)
+                       *(ptr++) = chip->trace_msg[idx].file[j];
+
+               for (j = 0; j < TIME_VAL_LEN; j++)
+                       *(ptr++) = chip->trace_msg[idx].timeval_buf[j];
+       }
+
+       rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
+       vfree(buf);
+
+       if (clear) {
+               chip->msg_idx = 0;
+               for (i = 0; i < TRACE_ITEM_CNT; i++)
+                       chip->trace_msg[i].valid = 0;
+       }
+
+       scsi_set_resid(srb, 0);
+       return TRANSPORT_GOOD;
+}
+#endif
+
+static int read_host_reg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       u8 addr, buf[4];
+       u32 val;
+       unsigned int len;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       addr = srb->cmnd[4];
+
+       val = rtsx_readl(chip, addr);
+       RTSX_DEBUGP("Host register (0x%x): 0x%x\n", addr, val);
+
+       buf[0] = (u8)(val >> 24);
+       buf[1] = (u8)(val >> 16);
+       buf[2] = (u8)(val >> 8);
+       buf[3] = (u8)val;
+
+       len = min_t(unsigned int, scsi_bufflen(srb), 4);
+       rtsx_stor_set_xfer_buf(buf, len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+       return TRANSPORT_GOOD;
+}
+
+static int write_host_reg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       u8 addr, buf[4];
+       u32 val;
+       unsigned int len;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       addr = srb->cmnd[4];
+
+       len = min_t(unsigned int, scsi_bufflen(srb), 4);
+       rtsx_stor_get_xfer_buf(buf, len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+       val = ((u32)buf[0] << 24) | ((u32)buf[1] << 16) | ((u32)buf[2]
+                                                       << 8) | buf[3];
+
+       rtsx_writel(chip, addr, val);
+
+       return TRANSPORT_GOOD;
+}
+
+static int set_variable(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned lun = SCSI_LUN(srb);
+
+       if (srb->cmnd[3] == 1) {
+               /* Variable Clock */
+               struct xd_info *xd_card = &(chip->xd_card);
+               struct sd_info *sd_card = &(chip->sd_card);
+               struct ms_info *ms_card = &(chip->ms_card);
+
+               switch (srb->cmnd[4]) {
+               case XD_CARD:
+                       xd_card->xd_clock = srb->cmnd[5];
+                       break;
+
+               case SD_CARD:
+                       sd_card->sd_clock = srb->cmnd[5];
+                       break;
+
+               case MS_CARD:
+                       ms_card->ms_clock = srb->cmnd[5];
+                       break;
+
+               default:
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       } else if (srb->cmnd[3] == 2) {
+               if (srb->cmnd[4]) {
+                       chip->blink_led = 1;
+               } else {
+                       int retval;
+
+                       chip->blink_led = 0;
+
+                       rtsx_disable_aspm(chip);
+
+                       if (chip->ss_en &&
+                               (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+                               rtsx_exit_ss(chip);
+                               wait_timeout(100);
+                       }
+                       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+                       retval = rtsx_force_power_on(chip, SSC_PDCTL);
+                       if (retval != STATUS_SUCCESS) {
+                               set_sense_type(chip, SCSI_LUN(srb),
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+                       }
+
+                       turn_off_led(chip, LED_GPIO);
+               }
+       } else {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       return TRANSPORT_GOOD;
+}
+
+static int get_variable(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned int lun = SCSI_LUN(srb);
+
+       if (srb->cmnd[3] == 1) {
+               struct xd_info *xd_card = &(chip->xd_card);
+               struct sd_info *sd_card = &(chip->sd_card);
+               struct ms_info *ms_card = &(chip->ms_card);
+               u8 tmp;
+
+               switch (srb->cmnd[4]) {
+               case XD_CARD:
+                       tmp = (u8)(xd_card->xd_clock);
+                       break;
+
+               case SD_CARD:
+                       tmp = (u8)(sd_card->sd_clock);
+                       break;
+
+               case MS_CARD:
+                       tmp = (u8)(ms_card->ms_clock);
+                       break;
+
+               default:
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+
+               rtsx_stor_set_xfer_buf(&tmp, 1, srb);
+       } else if (srb->cmnd[3] == 2) {
+               u8 tmp = chip->blink_led;
+               rtsx_stor_set_xfer_buf(&tmp, 1, srb);
+       } else {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       return TRANSPORT_GOOD;
+}
+
+static int dma_access_ring_buffer(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval;
+       unsigned int lun = SCSI_LUN(srb);
+       u16 len;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       len = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
+       len = min_t(u16, len, scsi_bufflen(srb));
+
+       if (srb->sc_data_direction == DMA_FROM_DEVICE)
+               RTSX_DEBUGP("Read from device\n");
+       else
+               RTSX_DEBUGP("Write to device\n");
+
+       retval = rtsx_transfer_data(chip, 0, scsi_sglist(srb), len,
+                       scsi_sg_count(srb), srb->sc_data_direction, 1000);
+       if (retval < 0) {
+               if (srb->sc_data_direction == DMA_FROM_DEVICE)
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+               else
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_WRITE_ERR);
+
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+       scsi_set_resid(srb, 0);
+
+       return TRANSPORT_GOOD;
+}
+
+static int get_dev_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       struct ms_info *ms_card = &(chip->ms_card);
+       int buf_len;
+       unsigned int lun = SCSI_LUN(srb);
+       u8 card = get_lun_card(chip, lun);
+       u8 status[32];
+#ifdef SUPPORT_OCP
+       u8 oc_now_mask = 0, oc_ever_mask = 0;
+#endif
+
+       memset(status, 0, 32);
+
+       status[0] = (u8)(chip->product_id);
+       status[1] = chip->ic_version;
+
+       if (chip->auto_delink_en)
+               status[2] = 0x10;
+       else
+               status[2] = 0x00;
+
+       status[3] = 20;
+       status[4] = 10;
+       status[5] = 05;
+       status[6] = 21;
+
+       if (chip->card_wp)
+               status[7] = 0x20;
+       else
+               status[7] = 0x00;
+
+#ifdef SUPPORT_OCP
+       status[8] = 0;
+       if (CHECK_LUN_MODE(chip,
+               SD_MS_2LUN) && (chip->lun2card[lun] == MS_CARD)) {
+               oc_now_mask = MS_OC_NOW;
+               oc_ever_mask = MS_OC_EVER;
+       } else {
+               oc_now_mask = SD_OC_NOW;
+               oc_ever_mask = SD_OC_EVER;
+       }
+
+       if (chip->ocp_stat & oc_now_mask)
+               status[8] |= 0x02;
+
+       if (chip->ocp_stat & oc_ever_mask)
+               status[8] |= 0x01;
+#endif
+
+       if (card == SD_CARD) {
+               if (CHK_SD(sd_card)) {
+                       if (CHK_SD_HCXC(sd_card)) {
+                               if (sd_card->capacity > 0x4000000)
+                                       status[0x0E] = 0x02;
+                               else
+                                       status[0x0E] = 0x01;
+                       } else {
+                               status[0x0E] = 0x00;
+                       }
+
+                       if (CHK_SD_SDR104(sd_card))
+                               status[0x0F] = 0x03;
+                       else if (CHK_SD_DDR50(sd_card))
+                               status[0x0F] = 0x04;
+                       else if (CHK_SD_SDR50(sd_card))
+                               status[0x0F] = 0x02;
+                       else if (CHK_SD_HS(sd_card))
+                               status[0x0F] = 0x01;
+                       else
+                               status[0x0F] = 0x00;
+               } else {
+                       if (CHK_MMC_SECTOR_MODE(sd_card))
+                               status[0x0E] = 0x01;
+                       else
+                               status[0x0E] = 0x00;
+
+                       if (CHK_MMC_DDR52(sd_card))
+                               status[0x0F] = 0x03;
+                       else if (CHK_MMC_52M(sd_card))
+                               status[0x0F] = 0x02;
+                       else if (CHK_MMC_26M(sd_card))
+                               status[0x0F] = 0x01;
+                       else
+                               status[0x0F] = 0x00;
+               }
+       } else if (card == MS_CARD) {
+               if (CHK_MSPRO(ms_card)) {
+                       if (CHK_MSXC(ms_card))
+                               status[0x0E] = 0x01;
+                       else
+                               status[0x0E] = 0x00;
+
+                       if (CHK_HG8BIT(ms_card))
+                               status[0x0F] = 0x01;
+                       else
+                               status[0x0F] = 0x00;
+               }
+       }
+
+#ifdef SUPPORT_SD_LOCK
+       if (card == SD_CARD) {
+               status[0x17] = 0x80;
+               if (sd_card->sd_erase_status)
+                       status[0x17] |= 0x01;
+               if (sd_card->sd_lock_status & SD_LOCKED) {
+                       status[0x17] |= 0x02;
+                       status[0x07] |= 0x40;
+               }
+               if (sd_card->sd_lock_status & SD_PWD_EXIST)
+                       status[0x17] |= 0x04;
+       } else {
+               status[0x17] = 0x00;
+       }
+
+       RTSX_DEBUGP("status[0x17] = 0x%x\n", status[0x17]);
+#endif
+
+       status[0x18] = 0x8A;
+       status[0x1A] = 0x28;
+#ifdef SUPPORT_SD_LOCK
+       status[0x1F] = 0x01;
+#endif
+
+       buf_len = min_t(unsigned int, scsi_bufflen(srb), sizeof(status));
+       rtsx_stor_set_xfer_buf(status, buf_len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);
+
+       return TRANSPORT_GOOD;
+}
+
+static int set_chip_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int phy_debug_mode;
+       int retval;
+       u16 reg;
+
+       if (!CHECK_PID(chip, 0x5208)) {
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       phy_debug_mode = (int)(srb->cmnd[3]);
+
+       if (phy_debug_mode) {
+               chip->phy_debug_mode = 1;
+               retval = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+
+               rtsx_disable_bus_int(chip);
+
+               retval = rtsx_read_phy_register(chip, 0x1C, &reg);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+
+               reg |= 0x0001;
+               retval = rtsx_write_phy_register(chip, 0x1C, reg);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+       } else {
+               chip->phy_debug_mode = 0;
+               retval = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0x77);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+
+               rtsx_enable_bus_int(chip);
+
+               retval = rtsx_read_phy_register(chip, 0x1C, &reg);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+
+               reg &= 0xFFFE;
+               retval = rtsx_write_phy_register(chip, 0x1C, reg);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       return TRANSPORT_GOOD;
+}
+
+static int rw_mem_cmd_buf(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval =  STATUS_SUCCESS;
+       unsigned int lun = SCSI_LUN(srb);
+       u8 cmd_type, mask, value, idx;
+       u16 addr;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       switch (srb->cmnd[3]) {
+       case INIT_BATCHCMD:
+               rtsx_init_cmd(chip);
+               break;
+
+       case ADD_BATCHCMD:
+               cmd_type = srb->cmnd[4];
+               if (cmd_type > 2) {
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+               addr = (srb->cmnd[5] << 8) | srb->cmnd[6];
+               mask = srb->cmnd[7];
+               value = srb->cmnd[8];
+               rtsx_add_cmd(chip, cmd_type, addr, mask, value);
+               break;
+
+       case SEND_BATCHCMD:
+               retval = rtsx_send_cmd(chip, 0, 1000);
+               break;
+
+       case GET_BATCHRSP:
+               idx = srb->cmnd[4];
+               value = *(rtsx_get_cmd_data(chip) + idx);
+               if (scsi_bufflen(srb) < 1) {
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+               rtsx_stor_set_xfer_buf(&value, 1, srb);
+               scsi_set_resid(srb, 0);
+               break;
+
+       default:
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       return TRANSPORT_GOOD;
+}
+
+static int suit_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int result;
+
+       switch (srb->cmnd[3]) {
+       case INIT_BATCHCMD:
+       case ADD_BATCHCMD:
+       case SEND_BATCHCMD:
+       case GET_BATCHRSP:
+               result = rw_mem_cmd_buf(srb, chip);
+               break;
+       default:
+               result = TRANSPORT_ERROR;
+       }
+
+       return result;
+}
+
+static int read_phy_register(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned short addr, len, i;
+       int retval;
+       u8 *buf;
+       u16 val;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+       len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
+
+       if (len % 2)
+               len -= len % 2;
+
+       if (len) {
+               buf = vmalloc(len);
+               if (!buf)
+                       TRACE_RET(chip, TRANSPORT_ERROR);
+
+               retval = rtsx_force_power_on(chip, SSC_PDCTL);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(buf);
+                       set_sense_type(chip, SCSI_LUN(srb),
+                               SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+
+               for (i = 0; i < len / 2; i++) {
+                       retval = rtsx_read_phy_register(chip, addr + i, &val);
+                       if (retval != STATUS_SUCCESS) {
+                               vfree(buf);
+                               set_sense_type(chip, SCSI_LUN(srb),
+                                       SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+                       }
+
+                       buf[2*i] = (u8)(val >> 8);
+                       buf[2*i+1] = (u8)val;
+               }
+
+               len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb),
+                                       len);
+               rtsx_stor_set_xfer_buf(buf, len, srb);
+               scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+               vfree(buf);
+       }
+
+       return TRANSPORT_GOOD;
+}
+
+static int write_phy_register(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned short addr, len, i;
+       int retval;
+       u8 *buf;
+       u16 val;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+       len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
+
+       if (len % 2)
+               len -= len % 2;
+
+       if (len) {
+               len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb),
+                                       len);
+
+               buf = vmalloc(len);
+               if (buf == NULL)
+                       TRACE_RET(chip, TRANSPORT_ERROR);
+
+               rtsx_stor_get_xfer_buf(buf, len, srb);
+               scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+               retval = rtsx_force_power_on(chip, SSC_PDCTL);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(buf);
+                       set_sense_type(chip, SCSI_LUN(srb),
+                               SENSE_TYPE_MEDIA_WRITE_ERR);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+
+               for (i = 0; i < len / 2; i++) {
+                       val = ((u16)buf[2*i] << 8) | buf[2*i+1];
+                       retval = rtsx_write_phy_register(chip, addr + i, val);
+                       if (retval != STATUS_SUCCESS) {
+                               vfree(buf);
+                               set_sense_type(chip, SCSI_LUN(srb),
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+                       }
+               }
+
+               vfree(buf);
+       }
+
+       return TRANSPORT_GOOD;
+}
+
+static int erase_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned short addr;
+       int retval;
+       u8 mode;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       retval = rtsx_force_power_on(chip, SSC_PDCTL);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       mode = srb->cmnd[3];
+       addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+
+       if (mode == 0) {
+               retval = spi_erase_eeprom_chip(chip);
+               if (retval != STATUS_SUCCESS) {
+                       set_sense_type(chip, SCSI_LUN(srb),
+                               SENSE_TYPE_MEDIA_WRITE_ERR);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       } else if (mode == 1) {
+               retval = spi_erase_eeprom_byte(chip, addr);
+               if (retval != STATUS_SUCCESS) {
+                       set_sense_type(chip, SCSI_LUN(srb),
+                               SENSE_TYPE_MEDIA_WRITE_ERR);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       } else {
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       return TRANSPORT_GOOD;
+}
+
+static int read_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned short addr, len, i;
+       int retval;
+       u8 *buf;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+       len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
+
+       buf = vmalloc(len);
+       if (!buf)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       retval = rtsx_force_power_on(chip, SSC_PDCTL);
+       if (retval != STATUS_SUCCESS) {
+               vfree(buf);
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       for (i = 0; i < len; i++) {
+               retval = spi_read_eeprom(chip, addr + i, buf + i);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(buf);
+                       set_sense_type(chip, SCSI_LUN(srb),
+                               SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+
+       len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
+       rtsx_stor_set_xfer_buf(buf, len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+       vfree(buf);
+
+       return TRANSPORT_GOOD;
+}
+
+static int write_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned short addr, len, i;
+       int retval;
+       u8 *buf;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+       len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
+
+       len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
+       buf = vmalloc(len);
+       if (buf == NULL)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       rtsx_stor_get_xfer_buf(buf, len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+       retval = rtsx_force_power_on(chip, SSC_PDCTL);
+       if (retval != STATUS_SUCCESS) {
+               vfree(buf);
+               set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       for (i = 0; i < len; i++) {
+               retval = spi_write_eeprom(chip, addr + i, buf[i]);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(buf);
+                       set_sense_type(chip, SCSI_LUN(srb),
+                               SENSE_TYPE_MEDIA_WRITE_ERR);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+
+       vfree(buf);
+
+       return TRANSPORT_GOOD;
+}
+
+static int read_efuse(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval;
+       u8 addr, len, i;
+       u8 *buf;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       addr = srb->cmnd[4];
+       len = srb->cmnd[5];
+
+       buf = vmalloc(len);
+       if (!buf)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       retval = rtsx_force_power_on(chip, SSC_PDCTL);
+       if (retval != STATUS_SUCCESS) {
+               vfree(buf);
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       for (i = 0; i < len; i++) {
+               retval = rtsx_read_efuse(chip, addr + i, buf + i);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(buf);
+                       set_sense_type(chip, SCSI_LUN(srb),
+                               SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+
+       len = (u8)min_t(unsigned int, scsi_bufflen(srb), len);
+       rtsx_stor_set_xfer_buf(buf, len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+       vfree(buf);
+
+       return TRANSPORT_GOOD;
+}
+
+static int write_efuse(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval, result = TRANSPORT_GOOD;
+       u16 val;
+       u8 addr, len, i;
+       u8 *buf;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       addr = srb->cmnd[4];
+       len = srb->cmnd[5];
+
+       len = (u8)min_t(unsigned int, scsi_bufflen(srb), len);
+       buf = vmalloc(len);
+       if (buf == NULL)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       rtsx_stor_get_xfer_buf(buf, len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+       retval = rtsx_force_power_on(chip, SSC_PDCTL);
+       if (retval != STATUS_SUCCESS) {
+               vfree(buf);
+               TRACE_RET(chip, TRANSPORT_ERROR);
+       }
+
+       if (chip->asic_code) {
+               retval = rtsx_read_phy_register(chip, 0x08, &val);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(buf);
+                       TRACE_RET(chip, TRANSPORT_ERROR);
+               }
+
+               retval = rtsx_write_register(chip, PWR_GATE_CTRL,
+                                       LDO3318_PWR_MASK, LDO_OFF);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(buf);
+                       TRACE_RET(chip, TRANSPORT_ERROR);
+               }
+
+               wait_timeout(600);
+
+               retval = rtsx_write_phy_register(chip, 0x08,
+                                               0x4C00 | chip->phy_voltage);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(buf);
+                       TRACE_RET(chip, TRANSPORT_ERROR);
+               }
+
+               retval = rtsx_write_register(chip, PWR_GATE_CTRL,
+                                       LDO3318_PWR_MASK, LDO_ON);
+               if (retval != STATUS_SUCCESS) {
+                       vfree(buf);
+                       TRACE_RET(chip, TRANSPORT_ERROR);
+               }
+
+               wait_timeout(600);
+       }
+
+       retval = card_power_on(chip, SPI_CARD);
+       if (retval != STATUS_SUCCESS) {
+               vfree(buf);
+               TRACE_RET(chip, TRANSPORT_ERROR);
+       }
+
+       wait_timeout(50);
+
+       for (i = 0; i < len; i++) {
+               retval = rtsx_write_efuse(chip, addr + i, buf[i]);
+               if (retval != STATUS_SUCCESS) {
+                       set_sense_type(chip, SCSI_LUN(srb),
+                               SENSE_TYPE_MEDIA_WRITE_ERR);
+                       result = TRANSPORT_FAILED;
+                       TRACE_GOTO(chip, Exit);
+               }
+       }
+
+Exit:
+       vfree(buf);
+
+       retval = card_power_off(chip, SPI_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       if (chip->asic_code) {
+               retval = rtsx_write_register(chip, PWR_GATE_CTRL,
+                                       LDO3318_PWR_MASK, LDO_OFF);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, TRANSPORT_ERROR);
+
+               wait_timeout(600);
+
+               retval = rtsx_write_phy_register(chip, 0x08, val);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, TRANSPORT_ERROR);
+
+               retval = rtsx_write_register(chip, PWR_GATE_CTRL,
+                                       LDO3318_PWR_MASK, LDO_ON);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, TRANSPORT_ERROR);
+       }
+
+       return result;
+}
+
+static int read_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval;
+       u8 func, func_max;
+       u16 addr, len;
+       u8 *buf;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       func = srb->cmnd[3];
+       addr = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
+       len = ((u16)(srb->cmnd[6]) << 8) | srb->cmnd[7];
+
+       RTSX_DEBUGP("%s: func = %d, addr = 0x%x, len = %d\n", __func__, func,
+               addr, len);
+
+       if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
+               func_max = 1;
+       else
+               func_max = 0;
+
+       if (func > func_max) {
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       buf = vmalloc(len);
+       if (!buf)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       retval = rtsx_read_cfg_seq(chip, func, addr, buf, len);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+               vfree(buf);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       len = (u16)min_t(unsigned int, scsi_bufflen(srb), len);
+       rtsx_stor_set_xfer_buf(buf, len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+       vfree(buf);
+
+       return TRANSPORT_GOOD;
+}
+
+static int write_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval;
+       u8 func, func_max;
+       u16 addr, len;
+       u8 *buf;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       func = srb->cmnd[3];
+       addr = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
+       len = ((u16)(srb->cmnd[6]) << 8) | srb->cmnd[7];
+
+       RTSX_DEBUGP("%s: func = %d, addr = 0x%x\n", __func__, func, addr);
+
+       if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
+               func_max = 1;
+       else
+               func_max = 0;
+
+       if (func > func_max) {
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
+       buf = vmalloc(len);
+       if (!buf)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       rtsx_stor_get_xfer_buf(buf, len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+       retval = rtsx_write_cfg_seq(chip, func, addr, buf, len);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+               vfree(buf);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       vfree(buf);
+
+       return TRANSPORT_GOOD;
+}
+
+static int app_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int result;
+
+       switch (srb->cmnd[2]) {
+       case PP_READ10:
+       case PP_WRITE10:
+               result = read_write(srb, chip);
+               break;
+
+       case READ_HOST_REG:
+               result = read_host_reg(srb, chip);
+               break;
+
+       case WRITE_HOST_REG:
+               result = write_host_reg(srb, chip);
+               break;
+
+       case GET_VAR:
+               result = get_variable(srb, chip);
+               break;
+
+       case SET_VAR:
+               result = set_variable(srb, chip);
+               break;
+
+       case DMA_READ:
+       case DMA_WRITE:
+               result = dma_access_ring_buffer(srb, chip);
+               break;
+
+       case READ_PHY:
+               result = read_phy_register(srb, chip);
+               break;
+
+       case WRITE_PHY:
+               result = write_phy_register(srb, chip);
+               break;
+
+       case ERASE_EEPROM2:
+               result = erase_eeprom2(srb, chip);
+               break;
+
+       case READ_EEPROM2:
+               result = read_eeprom2(srb, chip);
+               break;
+
+       case WRITE_EEPROM2:
+               result = write_eeprom2(srb, chip);
+               break;
+
+       case READ_EFUSE:
+               result = read_efuse(srb, chip);
+               break;
+
+       case WRITE_EFUSE:
+               result = write_efuse(srb, chip);
+               break;
+
+       case READ_CFG:
+               result = read_cfg_byte(srb, chip);
+               break;
+
+       case WRITE_CFG:
+               result = write_cfg_byte(srb, chip);
+               break;
+
+       case SET_CHIP_MODE:
+               result = set_chip_mode(srb, chip);
+               break;
+
+       case SUIT_CMD:
+               result = suit_cmd(srb, chip);
+               break;
+
+       case GET_DEV_STATUS:
+               result = get_dev_status(srb, chip);
+               break;
+
+       default:
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       return result;
+}
+
+
+static int read_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       u8 rtsx_status[16];
+       int buf_len;
+       unsigned int lun = SCSI_LUN(srb);
+
+       rtsx_status[0] = (u8)(chip->vendor_id >> 8);
+       rtsx_status[1] = (u8)(chip->vendor_id);
+
+       rtsx_status[2] = (u8)(chip->product_id >> 8);
+       rtsx_status[3] = (u8)(chip->product_id);
+
+       rtsx_status[4] = (u8)lun;
+
+       if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+               if (chip->lun2card[lun] == SD_CARD)
+                       rtsx_status[5] = 2;
+               else
+                       rtsx_status[5] = 3;
+       } else {
+               if (chip->card_exist) {
+                       if (chip->card_exist & XD_CARD)
+                               rtsx_status[5] = 4;
+                       else if (chip->card_exist & SD_CARD)
+                               rtsx_status[5] = 2;
+                       else if (chip->card_exist & MS_CARD)
+                               rtsx_status[5] = 3;
+                       else
+                               rtsx_status[5] = 7;
+               } else {
+                       rtsx_status[5] = 7;
+               }
+       }
+
+       if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
+               rtsx_status[6] = 2;
+       else
+               rtsx_status[6] = 1;
+
+       rtsx_status[7] = (u8)(chip->product_id);
+       rtsx_status[8] = chip->ic_version;
+
+       if (check_card_exist(chip, lun))
+               rtsx_status[9] = 1;
+       else
+               rtsx_status[9] = 0;
+
+       if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
+               rtsx_status[10] = 0;
+       else
+               rtsx_status[10] = 1;
+
+       if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+               if (chip->lun2card[lun] == SD_CARD)
+                       rtsx_status[11] = SD_CARD;
+               else
+                       rtsx_status[11] = MS_CARD;
+       } else {
+               rtsx_status[11] = XD_CARD | SD_CARD | MS_CARD;
+       }
+
+       if (check_card_ready(chip, lun))
+               rtsx_status[12] = 1;
+       else
+               rtsx_status[12] = 0;
+
+       if (get_lun_card(chip, lun) == XD_CARD) {
+               rtsx_status[13] = 0x40;
+       } else if (get_lun_card(chip, lun) == SD_CARD) {
+               struct sd_info *sd_card = &(chip->sd_card);
+
+               rtsx_status[13] = 0x20;
+               if (CHK_SD(sd_card)) {
+                       if (CHK_SD_HCXC(sd_card))
+                               rtsx_status[13] |= 0x04;
+                       if (CHK_SD_HS(sd_card))
+                               rtsx_status[13] |= 0x02;
+               } else {
+                       rtsx_status[13] |= 0x08;
+                       if (CHK_MMC_52M(sd_card))
+                               rtsx_status[13] |= 0x02;
+                       if (CHK_MMC_SECTOR_MODE(sd_card))
+                               rtsx_status[13] |= 0x04;
+               }
+       } else if (get_lun_card(chip, lun) == MS_CARD) {
+               struct ms_info *ms_card = &(chip->ms_card);
+
+               if (CHK_MSPRO(ms_card)) {
+                       rtsx_status[13] = 0x38;
+                       if (CHK_HG8BIT(ms_card))
+                               rtsx_status[13] |= 0x04;
+#ifdef SUPPORT_MSXC
+                       if (CHK_MSXC(ms_card))
+                               rtsx_status[13] |= 0x01;
+#endif
+               } else {
+                       rtsx_status[13] = 0x30;
+               }
+       } else {
+               if (CHECK_LUN_MODE(chip, DEFAULT_SINGLE)) {
+#ifdef SUPPORT_SDIO
+                       if (chip->sd_io && chip->sd_int)
+                               rtsx_status[13] = 0x60;
+                       else
+                               rtsx_status[13] = 0x70;
+#else
+                       rtsx_status[13] = 0x70;
+#endif
+               } else {
+                       if (chip->lun2card[lun] == SD_CARD)
+                               rtsx_status[13] = 0x20;
+                       else
+                               rtsx_status[13] = 0x30;
+               }
+       }
+
+       rtsx_status[14] = 0x78;
+       if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
+               rtsx_status[15] = 0x83;
+       else
+               rtsx_status[15] = 0x82;
+
+       buf_len = min_t(unsigned int, scsi_bufflen(srb), sizeof(rtsx_status));
+       rtsx_stor_set_xfer_buf(rtsx_status, buf_len, srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);
+
+       return TRANSPORT_GOOD;
+}
+
+static int get_card_bus_width(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned int lun = SCSI_LUN(srb);
+       u8 card, bus_width;
+
+       if (!check_card_ready(chip, lun)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       card = get_lun_card(chip, lun);
+       if ((card == SD_CARD) || (card == MS_CARD)) {
+               bus_width = chip->card_bus_width[lun];
+       } else {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       scsi_set_resid(srb, 0);
+       rtsx_stor_set_xfer_buf(&bus_width, scsi_bufflen(srb), srb);
+
+       return TRANSPORT_GOOD;
+}
+
+static int spi_vendor_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int result;
+       unsigned int lun = SCSI_LUN(srb);
+       u8 gpio_dir;
+
+       if (CHECK_PID(chip, 0x5208) || CHECK_PID(chip, 0x5288)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       rtsx_force_power_on(chip, SSC_PDCTL);
+
+       rtsx_read_register(chip, CARD_GPIO_DIR, &gpio_dir);
+       rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir & 0x06);
+
+       switch (srb->cmnd[2]) {
+       case SCSI_SPI_GETSTATUS:
+               result = spi_get_status(srb, chip);
+               break;
+
+       case SCSI_SPI_SETPARAMETER:
+               result = spi_set_parameter(srb, chip);
+               break;
+
+       case SCSI_SPI_READFALSHID:
+               result = spi_read_flash_id(srb, chip);
+               break;
+
+       case SCSI_SPI_READFLASH:
+               result = spi_read_flash(srb, chip);
+               break;
+
+       case SCSI_SPI_WRITEFLASH:
+               result = spi_write_flash(srb, chip);
+               break;
+
+       case SCSI_SPI_WRITEFLASHSTATUS:
+               result = spi_write_flash_status(srb, chip);
+               break;
+
+       case SCSI_SPI_ERASEFLASH:
+               result = spi_erase_flash(srb, chip);
+               break;
+
+       default:
+               rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir);
+
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir);
+
+       if (result != STATUS_SUCCESS)
+               TRACE_RET(chip, TRANSPORT_FAILED);
+
+       return TRANSPORT_GOOD;
+}
+
+static int vendor_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int result;
+
+       switch (srb->cmnd[1]) {
+       case READ_STATUS:
+               result = read_status(srb, chip);
+               break;
+
+       case READ_MEM:
+               result = read_mem(srb, chip);
+               break;
+
+       case WRITE_MEM:
+               result = write_mem(srb, chip);
+               break;
+
+       case READ_EEPROM:
+               result = read_eeprom(srb, chip);
+               break;
+
+       case WRITE_EEPROM:
+               result = write_eeprom(srb, chip);
+               break;
+
+       case TOGGLE_GPIO:
+               result = toggle_gpio_cmd(srb, chip);
+               break;
+
+       case GET_SD_CSD:
+               result = get_sd_csd(srb, chip);
+               break;
+
+       case GET_BUS_WIDTH:
+               result = get_card_bus_width(srb, chip);
+               break;
+
+#ifdef _MSG_TRACE
+       case TRACE_MSG:
+               result = trace_msg_cmd(srb, chip);
+               break;
+#endif
+
+       case SCSI_APP_CMD:
+               result = app_cmd(srb, chip);
+               break;
+
+       case SPI_VENDOR_COMMAND:
+               result = spi_vendor_cmd(srb, chip);
+               break;
+
+       default:
+               set_sense_type(chip, SCSI_LUN(srb),
+                       SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       return result;
+}
+
+#if !defined(LED_AUTO_BLINK) && !defined(REGULAR_BLINK)
+void led_shine(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned int lun = SCSI_LUN(srb);
+       u16 sec_cnt;
+
+       if ((srb->cmnd[0] == READ_10) || (srb->cmnd[0] == WRITE_10))
+               sec_cnt = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
+       else if ((srb->cmnd[0] == READ_6) || (srb->cmnd[0] == WRITE_6))
+               sec_cnt = srb->cmnd[4];
+       else
+               return;
+
+       if (chip->rw_cap[lun] >= GPIO_TOGGLE_THRESHOLD) {
+               toggle_gpio(chip, LED_GPIO);
+               chip->rw_cap[lun] = 0;
+       } else {
+               chip->rw_cap[lun] += sec_cnt;
+       }
+}
+#endif
+
+static int ms_format_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       unsigned int lun = SCSI_LUN(srb);
+       int retval, quick_format;
+
+       if (get_lun_card(chip, lun) != MS_CARD) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if ((srb->cmnd[3] != 0x4D) || (srb->cmnd[4] != 0x47) ||
+               (srb->cmnd[5] != 0x66) || (srb->cmnd[6] != 0x6D) ||
+               (srb->cmnd[7] != 0x74)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+
+               if (!check_card_ready(chip, lun) ||
+                               (get_card_size(chip, lun) == 0)) {
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       if (srb->cmnd[8] & 0x01)
+               quick_format = 0;
+       else
+               quick_format = 1;
+
+       if (!(chip->card_ready & MS_CARD)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (chip->card_wp & MS_CARD) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (!CHK_MSPRO(ms_card)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       retval = mspro_format(srb, chip, MS_SHORT_DATA_LEN, quick_format);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_FORMAT_CMD_FAILED);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       scsi_set_resid(srb, 0);
+       return TRANSPORT_GOOD;
+}
+
+#ifdef SUPPORT_PCGL_1P18
+static int get_ms_information(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       unsigned int lun = SCSI_LUN(srb);
+       u8 dev_info_id, data_len;
+       u8 *buf;
+       unsigned int buf_len;
+       int i;
+
+       if (!check_card_ready(chip, lun)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+       if ((get_lun_card(chip, lun) != MS_CARD)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if ((srb->cmnd[2] != 0xB0) || (srb->cmnd[4] != 0x4D) ||
+               (srb->cmnd[5] != 0x53) || (srb->cmnd[6] != 0x49) ||
+               (srb->cmnd[7] != 0x44)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       dev_info_id = srb->cmnd[3];
+       if ((CHK_MSXC(ms_card) && (dev_info_id == 0x10)) ||
+                       (!CHK_MSXC(ms_card) && (dev_info_id == 0x13)) ||
+                       !CHK_MSPRO(ms_card)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (dev_info_id == 0x15)
+               buf_len = data_len = 0x3A;
+       else
+               buf_len = data_len = 0x6A;
+
+       buf = kmalloc(buf_len, GFP_KERNEL);
+       if (!buf)
+               TRACE_RET(chip, TRANSPORT_ERROR);
+
+       i = 0;
+       /*  GET Memory Stick Media Information Response Header */
+       buf[i++] = 0x00;                /* Data length MSB */
+       buf[i++] = data_len;            /* Data length LSB */
+       /* Device Information Type Code */
+       if (CHK_MSXC(ms_card))
+               buf[i++] = 0x03;
+       else
+               buf[i++] = 0x02;
+
+       /* SGM bit */
+       buf[i++] = 0x01;
+       /* Reserved */
+       buf[i++] = 0x00;
+       buf[i++] = 0x00;
+       buf[i++] = 0x00;
+       /* Number of Device Information */
+       buf[i++] = 0x01;
+
+       /*  Device Information Body */
+
+       /* Device Information ID Number */
+       buf[i++] = dev_info_id;
+       /* Device Information Length */
+       if (dev_info_id == 0x15)
+               data_len = 0x31;
+       else
+               data_len = 0x61;
+
+       buf[i++] = 0x00;                /* Data length MSB */
+       buf[i++] = data_len;            /* Data length LSB */
+       /* Valid Bit */
+       buf[i++] = 0x80;
+       if ((dev_info_id == 0x10) || (dev_info_id == 0x13)) {
+               /* System Information */
+               memcpy(buf+i, ms_card->raw_sys_info, 96);
+       } else {
+               /* Model Name */
+               memcpy(buf+i, ms_card->raw_model_name, 48);
+       }
+
+       rtsx_stor_set_xfer_buf(buf, buf_len, srb);
+
+       if (dev_info_id == 0x15)
+               scsi_set_resid(srb, scsi_bufflen(srb)-0x3C);
+       else
+               scsi_set_resid(srb, scsi_bufflen(srb)-0x6C);
+
+       kfree(buf);
+       return STATUS_SUCCESS;
+}
+#endif
+
+static int ms_sp_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval = TRANSPORT_ERROR;
+
+       if (srb->cmnd[2] == MS_FORMAT)
+               retval = ms_format_cmnd(srb, chip);
+#ifdef SUPPORT_PCGL_1P18
+       else if (srb->cmnd[2] == GET_MS_INFORMATION)
+               retval = get_ms_information(srb, chip);
+#endif
+
+       return retval;
+}
+
+#ifdef SUPPORT_CPRM
+static int sd_extention_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       unsigned int lun = SCSI_LUN(srb);
+       int result;
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       sd_cleanup_work(chip);
+
+       if (!check_card_ready(chip, lun)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+       if ((get_lun_card(chip, lun) != SD_CARD)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       switch (srb->cmnd[0]) {
+       case SD_PASS_THRU_MODE:
+               result = sd_pass_thru_mode(srb, chip);
+               break;
+
+       case SD_EXECUTE_NO_DATA:
+               result = sd_execute_no_data(srb, chip);
+               break;
+
+       case SD_EXECUTE_READ:
+               result = sd_execute_read_data(srb, chip);
+               break;
+
+       case SD_EXECUTE_WRITE:
+               result = sd_execute_write_data(srb, chip);
+               break;
+
+       case SD_GET_RSP:
+               result = sd_get_cmd_rsp(srb, chip);
+               break;
+
+       case SD_HW_RST:
+               result = sd_hw_rst(srb, chip);
+               break;
+
+       default:
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       return result;
+}
+#endif
+
+#ifdef SUPPORT_MAGIC_GATE
+static int mg_report_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       unsigned int lun = SCSI_LUN(srb);
+       int retval;
+       u8 key_format;
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       ms_cleanup_work(chip);
+
+       if (!check_card_ready(chip, lun)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+       if ((get_lun_card(chip, lun) != MS_CARD)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (srb->cmnd[7] != KC_MG_R_PRO) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (!CHK_MSPRO(ms_card)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       key_format = srb->cmnd[10] & 0x3F;
+       RTSX_DEBUGP("key_format = 0x%x\n", key_format);
+
+       switch (key_format) {
+       case KF_GET_LOC_EKB:
+               if ((scsi_bufflen(srb) == 0x41C) &&
+                       (srb->cmnd[8] == 0x04) &&
+                       (srb->cmnd[9] == 0x1C)) {
+                       retval = mg_get_local_EKB(srb, chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+
+               } else {
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+               break;
+
+       case KF_RSP_CHG:
+               if ((scsi_bufflen(srb) == 0x24) &&
+                       (srb->cmnd[8] == 0x00) &&
+                       (srb->cmnd[9] == 0x24)) {
+                       retval = mg_get_rsp_chg(srb, chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+
+               } else {
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+               break;
+
+       case KF_GET_ICV:
+               ms_card->mg_entry_num = srb->cmnd[5];
+               if ((scsi_bufflen(srb) == 0x404) &&
+                       (srb->cmnd[8] == 0x04) &&
+                       (srb->cmnd[9] == 0x04) &&
+                       (srb->cmnd[2] == 0x00) &&
+                       (srb->cmnd[3] == 0x00) &&
+                       (srb->cmnd[4] == 0x00) &&
+                       (srb->cmnd[5] < 32)) {
+                       retval = mg_get_ICV(srb, chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+
+               } else {
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+               break;
+
+       default:
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       scsi_set_resid(srb, 0);
+       return TRANSPORT_GOOD;
+}
+
+static int mg_send_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct ms_info *ms_card = &(chip->ms_card);
+       unsigned int lun = SCSI_LUN(srb);
+       int retval;
+       u8 key_format;
+
+       RTSX_DEBUGP("--%s--\n", __func__);
+
+       rtsx_disable_aspm(chip);
+
+       if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+               rtsx_exit_ss(chip);
+               wait_timeout(100);
+       }
+       rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+       ms_cleanup_work(chip);
+
+       if (!check_card_ready(chip, lun)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+       if (check_card_wp(chip, lun)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+       if ((get_lun_card(chip, lun) != MS_CARD)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (srb->cmnd[7] != KC_MG_R_PRO) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (!CHK_MSPRO(ms_card)) {
+               set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       key_format = srb->cmnd[10] & 0x3F;
+       RTSX_DEBUGP("key_format = 0x%x\n", key_format);
+
+       switch (key_format) {
+       case KF_SET_LEAF_ID:
+               if ((scsi_bufflen(srb) == 0x0C) &&
+                       (srb->cmnd[8] == 0x00) &&
+                       (srb->cmnd[9] == 0x0C)) {
+                       retval = mg_set_leaf_id(srb, chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+
+               } else {
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+               break;
+
+       case KF_CHG_HOST:
+               if ((scsi_bufflen(srb) == 0x0C) &&
+                       (srb->cmnd[8] == 0x00) &&
+                       (srb->cmnd[9] == 0x0C)) {
+                       retval = mg_chg(srb, chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+
+               } else {
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+               break;
+
+       case KF_RSP_HOST:
+               if ((scsi_bufflen(srb) == 0x0C) &&
+                       (srb->cmnd[8] == 0x00) &&
+                       (srb->cmnd[9] == 0x0C)) {
+                       retval = mg_rsp(srb, chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+
+               } else {
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+               break;
+
+       case KF_SET_ICV:
+               ms_card->mg_entry_num = srb->cmnd[5];
+               if ((scsi_bufflen(srb) == 0x404) &&
+                       (srb->cmnd[8] == 0x04) &&
+                       (srb->cmnd[9] == 0x04) &&
+                       (srb->cmnd[2] == 0x00) &&
+                       (srb->cmnd[3] == 0x00) &&
+                       (srb->cmnd[4] == 0x00) &&
+                       (srb->cmnd[5] < 32)) {
+                       retval = mg_set_ICV(srb, chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+
+               } else {
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+               break;
+
+       default:
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       scsi_set_resid(srb, 0);
+       return TRANSPORT_GOOD;
+}
+#endif
+
+int rtsx_scsi_handler(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+#ifdef SUPPORT_SD_LOCK
+       struct sd_info *sd_card = &(chip->sd_card);
+#endif
+       struct ms_info *ms_card = &(chip->ms_card);
+       unsigned int lun = SCSI_LUN(srb);
+       int result;
+
+#ifdef SUPPORT_SD_LOCK
+       if (sd_card->sd_erase_status) {
+               /* Block all SCSI command except for
+                * REQUEST_SENSE and rs_ppstatus
+                */
+               if (!((srb->cmnd[0] == VENDOR_CMND) &&
+                               (srb->cmnd[1] == SCSI_APP_CMD) &&
+                               (srb->cmnd[2] == GET_DEV_STATUS)) &&
+                               (srb->cmnd[0] != REQUEST_SENSE)) {
+                       /* Logical Unit Not Ready Format in Progress */
+                       set_sense_data(chip, lun, CUR_ERR,
+                                      0x02, 0, 0x04, 0x04, 0, 0);
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+#endif
+
+       if ((get_lun_card(chip, lun) == MS_CARD) &&
+                       (ms_card->format_status == FORMAT_IN_PROGRESS)) {
+               if ((srb->cmnd[0] != REQUEST_SENSE) &&
+                       (srb->cmnd[0] != INQUIRY)) {
+                       /* Logical Unit Not Ready Format in Progress */
+                       set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04,
+                                       0, (u16)(ms_card->progress));
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+
+       switch (srb->cmnd[0]) {
+       case READ_10:
+       case WRITE_10:
+       case READ_6:
+       case WRITE_6:
+               result = read_write(srb, chip);
+#if !defined(LED_AUTO_BLINK) && !defined(REGULAR_BLINK)
+               led_shine(srb, chip);
+#endif
+               break;
+
+       case TEST_UNIT_READY:
+               result = test_unit_ready(srb, chip);
+               break;
+
+       case INQUIRY:
+               result = inquiry(srb, chip);
+               break;
+
+       case READ_CAPACITY:
+               result = read_capacity(srb, chip);
+               break;
+
+       case START_STOP:
+               result = start_stop_unit(srb, chip);
+               break;
+
+       case ALLOW_MEDIUM_REMOVAL:
+               result = allow_medium_removal(srb, chip);
+               break;
+
+       case REQUEST_SENSE:
+               result = request_sense(srb, chip);
+               break;
+
+       case MODE_SENSE:
+       case MODE_SENSE_10:
+               result = mode_sense(srb, chip);
+               break;
+
+       case 0x23:
+               result = read_format_capacity(srb, chip);
+               break;
+
+       case VENDOR_CMND:
+               result = vendor_cmnd(srb, chip);
+               break;
+
+       case MS_SP_CMND:
+               result = ms_sp_cmnd(srb, chip);
+               break;
+
+#ifdef SUPPORT_CPRM
+       case SD_PASS_THRU_MODE:
+       case SD_EXECUTE_NO_DATA:
+       case SD_EXECUTE_READ:
+       case SD_EXECUTE_WRITE:
+       case SD_GET_RSP:
+       case SD_HW_RST:
+               result = sd_extention_cmnd(srb, chip);
+               break;
+#endif
+
+#ifdef SUPPORT_MAGIC_GATE
+       case CMD_MSPRO_MG_RKEY:
+               result = mg_report_key(srb, chip);
+               break;
+
+       case CMD_MSPRO_MG_SKEY:
+               result = mg_send_key(srb, chip);
+               break;
+#endif
+
+       case FORMAT_UNIT:
+       case MODE_SELECT:
+       case VERIFY:
+               result = TRANSPORT_GOOD;
+               break;
+
+       default:
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               result = TRANSPORT_FAILED;
+       }
+
+       return result;
+}
diff --git a/drivers/staging/rts5208/rtsx_scsi.h b/drivers/staging/rts5208/rtsx_scsi.h
new file mode 100644 (file)
index 0000000..d175057
--- /dev/null
@@ -0,0 +1,143 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_SCSI_H
+#define __REALTEK_RTSX_SCSI_H
+
+#include "rtsx.h"
+#include "rtsx_chip.h"
+
+#define MS_SP_CMND             0xFA
+#define MS_FORMAT              0xA0
+#define GET_MS_INFORMATION     0xB0
+
+#define VENDOR_CMND            0xF0
+
+#define READ_STATUS            0x09
+
+#define READ_EEPROM            0x04
+#define WRITE_EEPROM           0x05
+#define READ_MEM               0x0D
+#define WRITE_MEM              0x0E
+#define GET_BUS_WIDTH          0x13
+#define GET_SD_CSD             0x14
+#define TOGGLE_GPIO            0x15
+#define TRACE_MSG              0x18
+
+#define SCSI_APP_CMD           0x10
+
+#define PP_READ10              0x1A
+#define PP_WRITE10             0x0A
+#define READ_HOST_REG          0x1D
+#define WRITE_HOST_REG         0x0D
+#define SET_VAR                        0x05
+#define GET_VAR                        0x15
+#define DMA_READ               0x16
+#define DMA_WRITE              0x06
+#define GET_DEV_STATUS         0x10
+#define SET_CHIP_MODE          0x27
+#define SUIT_CMD               0xE0
+#define WRITE_PHY              0x07
+#define READ_PHY               0x17
+#define WRITE_EEPROM2          0x03
+#define READ_EEPROM2           0x13
+#define ERASE_EEPROM2          0x23
+#define WRITE_EFUSE            0x04
+#define READ_EFUSE             0x14
+#define WRITE_CFG              0x0E
+#define READ_CFG               0x1E
+
+#define SPI_VENDOR_COMMAND             0x1C
+
+#define        SCSI_SPI_GETSTATUS              0x00
+#define        SCSI_SPI_SETPARAMETER           0x01
+#define        SCSI_SPI_READFALSHID            0x02
+#define        SCSI_SPI_READFLASH              0x03
+#define        SCSI_SPI_WRITEFLASH             0x04
+#define        SCSI_SPI_WRITEFLASHSTATUS       0x05
+#define        SCSI_SPI_ERASEFLASH             0x06
+
+#define INIT_BATCHCMD          0x41
+#define ADD_BATCHCMD           0x42
+#define SEND_BATCHCMD          0x43
+#define GET_BATCHRSP           0x44
+
+#define CHIP_NORMALMODE                0x00
+#define CHIP_DEBUGMODE         0x01
+
+/* SD Pass Through Command Extension */
+#define SD_PASS_THRU_MODE      0xD0
+#define SD_EXECUTE_NO_DATA     0xD1
+#define SD_EXECUTE_READ                0xD2
+#define SD_EXECUTE_WRITE       0xD3
+#define SD_GET_RSP             0xD4
+#define SD_HW_RST              0xD6
+
+#ifdef SUPPORT_MAGIC_GATE
+#define CMD_MSPRO_MG_RKEY      0xA4   /* Report Key Command */
+#define CMD_MSPRO_MG_SKEY      0xA3   /* Send Key Command */
+
+/* CBWCB field: key class */
+#define KC_MG_R_PRO            0xBE   /* MG-R PRO*/
+
+/* CBWCB field: key format */
+#define KF_SET_LEAF_ID         0x31   /* Set Leaf ID */
+#define KF_GET_LOC_EKB         0x32   /* Get Local EKB */
+#define KF_CHG_HOST            0x33   /* Challenge (host) */
+#define KF_RSP_CHG             0x34   /* Response and Challenge (device)  */
+#define KF_RSP_HOST            0x35   /* Response (host) */
+#define KF_GET_ICV             0x36   /* Get ICV */
+#define KF_SET_ICV             0x37   /* SSet ICV */
+#endif
+
+/* Sense type */
+#define        SENSE_TYPE_NO_SENSE                             0
+#define        SENSE_TYPE_MEDIA_CHANGE                         1
+#define        SENSE_TYPE_MEDIA_NOT_PRESENT                    2
+#define        SENSE_TYPE_MEDIA_LBA_OVER_RANGE                 3
+#define        SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT                4
+#define        SENSE_TYPE_MEDIA_WRITE_PROTECT                  5
+#define        SENSE_TYPE_MEDIA_INVALID_CMD_FIELD              6
+#define        SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR             7
+#define        SENSE_TYPE_MEDIA_WRITE_ERR                      8
+#define SENSE_TYPE_FORMAT_IN_PROGRESS                  9
+#define SENSE_TYPE_FORMAT_CMD_FAILED                   10
+#ifdef SUPPORT_MAGIC_GATE
+#define SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB               0x0b
+#define SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN              0x0c
+#define SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM              0x0d
+#define SENSE_TYPE_MG_WRITE_ERR                                0x0e
+#endif
+#ifdef SUPPORT_SD_LOCK
+/* FOR Locked SD card*/
+#define SENSE_TYPE_MEDIA_READ_FORBIDDEN                        0x10
+#endif
+
+void scsi_show_command(struct scsi_cmnd *srb);
+void set_sense_type(struct rtsx_chip *chip, unsigned int lun, int sense_type);
+void set_sense_data(struct rtsx_chip *chip, unsigned int lun, u8 err_code,
+               u8 sense_key, u32 info, u8 asc, u8 ascq,
+               u8 sns_key_info0, u16 sns_key_info1);
+int rtsx_scsi_handler(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+
+#endif   /* __REALTEK_RTSX_SCSI_H */
diff --git a/drivers/staging/rts5208/rtsx_sys.h b/drivers/staging/rts5208/rtsx_sys.h
new file mode 100644 (file)
index 0000000..0b6b4d4
--- /dev/null
@@ -0,0 +1,50 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __RTSX_SYS_H
+#define __RTSX_SYS_H
+
+#include "rtsx.h"
+#include "rtsx_chip.h"
+#include "rtsx_card.h"
+
+typedef dma_addr_t ULONG_PTR;
+
+static inline void rtsx_exclusive_enter_ss(struct rtsx_chip *chip)
+{
+       struct rtsx_dev *dev = chip->rtsx;
+
+       spin_lock(&(dev->reg_lock));
+       rtsx_enter_ss(chip);
+       spin_unlock(&(dev->reg_lock));
+}
+
+static inline void rtsx_reset_detected_cards(struct rtsx_chip *chip, int flag)
+{
+       rtsx_reset_cards(chip);
+}
+
+#define RTSX_MSG_IN_INT(x)
+
+#endif  /* __RTSX_SYS_H */
+
diff --git a/drivers/staging/rts5208/rtsx_transport.c b/drivers/staging/rts5208/rtsx_transport.c
new file mode 100644 (file)
index 0000000..97b7b01
--- /dev/null
@@ -0,0 +1,769 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+
+#include "rtsx.h"
+#include "rtsx_scsi.h"
+#include "rtsx_transport.h"
+#include "rtsx_chip.h"
+#include "rtsx_card.h"
+#include "debug.h"
+
+/***********************************************************************
+ * Scatter-gather transfer buffer access routines
+ ***********************************************************************/
+
+/* Copy a buffer of length buflen to/from the srb's transfer buffer.
+ * (Note: for scatter-gather transfers (srb->use_sg > 0), srb->request_buffer
+ * points to a list of s-g entries and we ignore srb->request_bufflen.
+ * For non-scatter-gather transfers, srb->request_buffer points to the
+ * transfer buffer itself and srb->request_bufflen is the buffer's length.)
+ * Update the *index and *offset variables so that the next copy will
+ * pick up from where this one left off. */
+
+unsigned int rtsx_stor_access_xfer_buf(unsigned char *buffer,
+       unsigned int buflen, struct scsi_cmnd *srb, unsigned int *index,
+       unsigned int *offset, enum xfer_buf_dir dir)
+{
+       unsigned int cnt;
+
+       /* If not using scatter-gather, just transfer the data directly.
+        * Make certain it will fit in the available buffer space. */
+       if (scsi_sg_count(srb) == 0) {
+               if (*offset >= scsi_bufflen(srb))
+                       return 0;
+               cnt = min(buflen, scsi_bufflen(srb) - *offset);
+               if (dir == TO_XFER_BUF)
+                       memcpy((unsigned char *) scsi_sglist(srb) + *offset,
+                                       buffer, cnt);
+               else
+                       memcpy(buffer, (unsigned char *) scsi_sglist(srb) +
+                                       *offset, cnt);
+               *offset += cnt;
+
+       /* Using scatter-gather.  We have to go through the list one entry
+        * at a time.  Each s-g entry contains some number of pages, and
+        * each page has to be kmap()'ed separately.  If the page is already
+        * in kernel-addressable memory then kmap() will return its address.
+        * If the page is not directly accessible -- such as a user buffer
+        * located in high memory -- then kmap() will map it to a temporary
+        * position in the kernel's virtual address space. */
+       } else {
+               struct scatterlist *sg =
+                               (struct scatterlist *) scsi_sglist(srb)
+                               + *index;
+
+               /* This loop handles a single s-g list entry, which may
+                * include multiple pages.  Find the initial page structure
+                * and the starting offset within the page, and update
+                * the *offset and *index values for the next loop. */
+               cnt = 0;
+               while (cnt < buflen && *index < scsi_sg_count(srb)) {
+                       struct page *page = sg_page(sg) +
+                                       ((sg->offset + *offset) >> PAGE_SHIFT);
+                       unsigned int poff =
+                                       (sg->offset + *offset) & (PAGE_SIZE-1);
+                       unsigned int sglen = sg->length - *offset;
+
+                       if (sglen > buflen - cnt) {
+
+                               /* Transfer ends within this s-g entry */
+                               sglen = buflen - cnt;
+                               *offset += sglen;
+                       } else {
+
+                               /* Transfer continues to next s-g entry */
+                               *offset = 0;
+                               ++*index;
+                               ++sg;
+                       }
+
+                       /* Transfer the data for all the pages in this
+                        * s-g entry.  For each page: call kmap(), do the
+                        * transfer, and call kunmap() immediately after. */
+                       while (sglen > 0) {
+                               unsigned int plen = min(sglen, (unsigned int)
+                                               PAGE_SIZE - poff);
+                               unsigned char *ptr = kmap(page);
+
+                               if (dir == TO_XFER_BUF)
+                                       memcpy(ptr + poff, buffer + cnt, plen);
+                               else
+                                       memcpy(buffer + cnt, ptr + poff, plen);
+                               kunmap(page);
+
+                               /* Start at the beginning of the next page */
+                               poff = 0;
+                               ++page;
+                               cnt += plen;
+                               sglen -= plen;
+                       }
+               }
+       }
+
+       /* Return the amount actually transferred */
+       return cnt;
+}
+
+/* Store the contents of buffer into srb's transfer buffer and set the
+* SCSI residue. */
+void rtsx_stor_set_xfer_buf(unsigned char *buffer,
+       unsigned int buflen, struct scsi_cmnd *srb)
+{
+       unsigned int index = 0, offset = 0;
+
+       rtsx_stor_access_xfer_buf(buffer, buflen, srb, &index, &offset,
+                                 TO_XFER_BUF);
+       if (buflen < scsi_bufflen(srb))
+               scsi_set_resid(srb, scsi_bufflen(srb) - buflen);
+}
+
+void rtsx_stor_get_xfer_buf(unsigned char *buffer,
+       unsigned int buflen, struct scsi_cmnd *srb)
+{
+       unsigned int index = 0, offset = 0;
+
+       rtsx_stor_access_xfer_buf(buffer, buflen, srb, &index, &offset,
+                                 FROM_XFER_BUF);
+       if (buflen < scsi_bufflen(srb))
+               scsi_set_resid(srb, scsi_bufflen(srb) - buflen);
+}
+
+
+/***********************************************************************
+ * Transport routines
+ ***********************************************************************/
+
+/* Invoke the transport and basic error-handling/recovery methods
+ *
+ * This is used to send the message to the device and receive the response.
+ */
+void rtsx_invoke_transport(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int result;
+
+       result = rtsx_scsi_handler(srb, chip);
+
+       /* if the command gets aborted by the higher layers, we need to
+        * short-circuit all other processing
+        */
+       if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) {
+               RTSX_DEBUGP("-- command was aborted\n");
+               srb->result = DID_ABORT << 16;
+               goto Handle_Errors;
+       }
+
+       /* if there is a transport error, reset and don't auto-sense */
+       if (result == TRANSPORT_ERROR) {
+               RTSX_DEBUGP("-- transport indicates error, resetting\n");
+               srb->result = DID_ERROR << 16;
+               goto Handle_Errors;
+       }
+
+       srb->result = SAM_STAT_GOOD;
+
+       /*
+        * If we have a failure, we're going to do a REQUEST_SENSE
+        * automatically.  Note that we differentiate between a command
+        * "failure" and an "error" in the transport mechanism.
+        */
+       if (result == TRANSPORT_FAILED) {
+               /* set the result so the higher layers expect this data */
+               srb->result = SAM_STAT_CHECK_CONDITION;
+               memcpy(srb->sense_buffer,
+                       (unsigned char *)&(chip->sense_buffer[SCSI_LUN(srb)]),
+                       sizeof(struct sense_data_t));
+       }
+
+       return;
+
+       /* Error and abort processing: try to resynchronize with the device
+        * by issuing a port reset.  If that fails, try a class-specific
+        * device reset. */
+Handle_Errors:
+       return;
+}
+
+void rtsx_add_cmd(struct rtsx_chip *chip,
+               u8 cmd_type, u16 reg_addr, u8 mask, u8 data)
+{
+       u32 *cb = (u32 *)(chip->host_cmds_ptr);
+       u32 val = 0;
+
+       val |= (u32)(cmd_type & 0x03) << 30;
+       val |= (u32)(reg_addr & 0x3FFF) << 16;
+       val |= (u32)mask << 8;
+       val |= (u32)data;
+
+       spin_lock_irq(&chip->rtsx->reg_lock);
+       if (chip->ci < (HOST_CMDS_BUF_LEN / 4))
+               cb[(chip->ci)++] = cpu_to_le32(val);
+
+       spin_unlock_irq(&chip->rtsx->reg_lock);
+}
+
+void rtsx_send_cmd_no_wait(struct rtsx_chip *chip)
+{
+       u32 val = 1 << 31;
+
+       rtsx_writel(chip, RTSX_HCBAR, chip->host_cmds_addr);
+
+       val |= (u32)(chip->ci * 4) & 0x00FFFFFF;
+       /* Hardware Auto Response */
+       val |= 0x40000000;
+       rtsx_writel(chip, RTSX_HCBCTLR, val);
+}
+
+int rtsx_send_cmd(struct rtsx_chip *chip, u8 card, int timeout)
+{
+       struct rtsx_dev *rtsx = chip->rtsx;
+       struct completion trans_done;
+       u32 val = 1 << 31;
+       long timeleft;
+       int err = 0;
+
+       if (card == SD_CARD)
+               rtsx->check_card_cd = SD_EXIST;
+       else if (card == MS_CARD)
+               rtsx->check_card_cd = MS_EXIST;
+       else if (card == XD_CARD)
+               rtsx->check_card_cd = XD_EXIST;
+       else
+               rtsx->check_card_cd = 0;
+
+       spin_lock_irq(&rtsx->reg_lock);
+
+       /* set up data structures for the wakeup system */
+       rtsx->done = &trans_done;
+       rtsx->trans_result = TRANS_NOT_READY;
+       init_completion(&trans_done);
+       rtsx->trans_state = STATE_TRANS_CMD;
+
+       rtsx_writel(chip, RTSX_HCBAR, chip->host_cmds_addr);
+
+       val |= (u32)(chip->ci * 4) & 0x00FFFFFF;
+       /* Hardware Auto Response */
+       val |= 0x40000000;
+       rtsx_writel(chip, RTSX_HCBCTLR, val);
+
+       spin_unlock_irq(&rtsx->reg_lock);
+
+       /* Wait for TRANS_OK_INT */
+       timeleft = wait_for_completion_interruptible_timeout(
+               &trans_done, timeout * HZ / 1000);
+       if (timeleft <= 0) {
+               RTSX_DEBUGP("chip->int_reg = 0x%x\n", chip->int_reg);
+               err = -ETIMEDOUT;
+               TRACE_GOTO(chip, finish_send_cmd);
+       }
+
+       spin_lock_irq(&rtsx->reg_lock);
+       if (rtsx->trans_result == TRANS_RESULT_FAIL)
+               err = -EIO;
+       else if (rtsx->trans_result == TRANS_RESULT_OK)
+               err = 0;
+
+       spin_unlock_irq(&rtsx->reg_lock);
+
+finish_send_cmd:
+       rtsx->done = NULL;
+       rtsx->trans_state = STATE_TRANS_NONE;
+
+       if (err < 0)
+               rtsx_stop_cmd(chip, card);
+
+       return err;
+}
+
+static inline void rtsx_add_sg_tbl(
+       struct rtsx_chip *chip, u32 addr, u32 len, u8 option)
+{
+       u64 *sgb = (u64 *)(chip->host_sg_tbl_ptr);
+       u64 val = 0;
+       u32 temp_len = 0;
+       u8  temp_opt = 0;
+
+       do {
+               if (len > 0x80000) {
+                       temp_len = 0x80000;
+                       temp_opt = option & (~SG_END);
+               } else {
+                       temp_len = len;
+                       temp_opt = option;
+               }
+               val = ((u64)addr << 32) | ((u64)temp_len << 12) | temp_opt;
+
+               if (chip->sgi < (HOST_SG_TBL_BUF_LEN / 8))
+                       sgb[(chip->sgi)++] = cpu_to_le64(val);
+
+               len -= temp_len;
+               addr += temp_len;
+       } while (len);
+}
+
+static int rtsx_transfer_sglist_adma_partial(struct rtsx_chip *chip, u8 card,
+               struct scatterlist *sg, int num_sg, unsigned int *index,
+               unsigned int *offset, int size,
+               enum dma_data_direction dma_dir, int timeout)
+{
+       struct rtsx_dev *rtsx = chip->rtsx;
+       struct completion trans_done;
+       u8 dir;
+       int sg_cnt, i, resid;
+       int err = 0;
+       long timeleft;
+       struct scatterlist *sg_ptr;
+       u32 val = TRIG_DMA;
+
+       if ((sg == NULL) || (num_sg <= 0) || !offset || !index)
+               return -EIO;
+
+       if (dma_dir == DMA_TO_DEVICE)
+               dir = HOST_TO_DEVICE;
+       else if (dma_dir == DMA_FROM_DEVICE)
+               dir = DEVICE_TO_HOST;
+       else
+               return -ENXIO;
+
+       if (card == SD_CARD)
+               rtsx->check_card_cd = SD_EXIST;
+       else if (card == MS_CARD)
+               rtsx->check_card_cd = MS_EXIST;
+       else if (card == XD_CARD)
+               rtsx->check_card_cd = XD_EXIST;
+       else
+               rtsx->check_card_cd = 0;
+
+       spin_lock_irq(&rtsx->reg_lock);
+
+       /* set up data structures for the wakeup system */
+       rtsx->done = &trans_done;
+
+       rtsx->trans_state = STATE_TRANS_SG;
+       rtsx->trans_result = TRANS_NOT_READY;
+
+       spin_unlock_irq(&rtsx->reg_lock);
+
+       sg_cnt = dma_map_sg(&(rtsx->pci->dev), sg, num_sg, dma_dir);
+
+       resid = size;
+       sg_ptr = sg;
+       chip->sgi = 0;
+       /* Usually the next entry will be @sg@ + 1, but if this sg element
+        * is part of a chained scatterlist, it could jump to the start of
+        * a new scatterlist array. So here we use sg_next to move to
+        * the proper sg
+        */
+       for (i = 0; i < *index; i++)
+               sg_ptr = sg_next(sg_ptr);
+       for (i = *index; i < sg_cnt; i++) {
+               dma_addr_t addr;
+               unsigned int len;
+               u8 option;
+
+               addr = sg_dma_address(sg_ptr);
+               len = sg_dma_len(sg_ptr);
+
+               RTSX_DEBUGP("DMA addr: 0x%x, Len: 0x%x\n",
+                            (unsigned int)addr, len);
+               RTSX_DEBUGP("*index = %d, *offset = %d\n", *index, *offset);
+
+               addr += *offset;
+
+               if ((len - *offset) > resid) {
+                       *offset += resid;
+                       len = resid;
+                       resid = 0;
+               } else {
+                       resid -= (len - *offset);
+                       len -= *offset;
+                       *offset = 0;
+                       *index = *index + 1;
+               }
+               if ((i == (sg_cnt - 1)) || !resid)
+                       option = SG_VALID | SG_END | SG_TRANS_DATA;
+               else
+                       option = SG_VALID | SG_TRANS_DATA;
+
+               rtsx_add_sg_tbl(chip, (u32)addr, (u32)len, option);
+
+               if (!resid)
+                       break;
+
+               sg_ptr = sg_next(sg_ptr);
+       }
+
+       RTSX_DEBUGP("SG table count = %d\n", chip->sgi);
+
+       val |= (u32)(dir & 0x01) << 29;
+       val |= ADMA_MODE;
+
+       spin_lock_irq(&rtsx->reg_lock);
+
+       init_completion(&trans_done);
+
+       rtsx_writel(chip, RTSX_HDBAR, chip->host_sg_tbl_addr);
+       rtsx_writel(chip, RTSX_HDBCTLR, val);
+
+       spin_unlock_irq(&rtsx->reg_lock);
+
+       timeleft = wait_for_completion_interruptible_timeout(
+               &trans_done, timeout * HZ / 1000);
+       if (timeleft <= 0) {
+               RTSX_DEBUGP("Timeout (%s %d)\n", __func__, __LINE__);
+               RTSX_DEBUGP("chip->int_reg = 0x%x\n", chip->int_reg);
+               err = -ETIMEDOUT;
+               goto out;
+       }
+
+       spin_lock_irq(&rtsx->reg_lock);
+       if (rtsx->trans_result == TRANS_RESULT_FAIL) {
+               err = -EIO;
+               spin_unlock_irq(&rtsx->reg_lock);
+               goto out;
+       }
+       spin_unlock_irq(&rtsx->reg_lock);
+
+       /* Wait for TRANS_OK_INT */
+       spin_lock_irq(&rtsx->reg_lock);
+       if (rtsx->trans_result == TRANS_NOT_READY) {
+               init_completion(&trans_done);
+               spin_unlock_irq(&rtsx->reg_lock);
+               timeleft = wait_for_completion_interruptible_timeout(
+                       &trans_done, timeout * HZ / 1000);
+               if (timeleft <= 0) {
+                       RTSX_DEBUGP("Timeout (%s %d)\n", __func__, __LINE__);
+                       RTSX_DEBUGP("chip->int_reg = 0x%x\n", chip->int_reg);
+                       err = -ETIMEDOUT;
+                       goto out;
+               }
+       } else {
+               spin_unlock_irq(&rtsx->reg_lock);
+       }
+
+       spin_lock_irq(&rtsx->reg_lock);
+       if (rtsx->trans_result == TRANS_RESULT_FAIL)
+               err = -EIO;
+       else if (rtsx->trans_result == TRANS_RESULT_OK)
+               err = 0;
+
+       spin_unlock_irq(&rtsx->reg_lock);
+
+out:
+       rtsx->done = NULL;
+       rtsx->trans_state = STATE_TRANS_NONE;
+       dma_unmap_sg(&(rtsx->pci->dev), sg, num_sg, dma_dir);
+
+       if (err < 0)
+               rtsx_stop_cmd(chip, card);
+
+       return err;
+}
+
+static int rtsx_transfer_sglist_adma(struct rtsx_chip *chip, u8 card,
+               struct scatterlist *sg, int num_sg,
+               enum dma_data_direction dma_dir, int timeout)
+{
+       struct rtsx_dev *rtsx = chip->rtsx;
+       struct completion trans_done;
+       u8 dir;
+       int buf_cnt, i;
+       int err = 0;
+       long timeleft;
+       struct scatterlist *sg_ptr;
+
+       if ((sg == NULL) || (num_sg <= 0))
+               return -EIO;
+
+       if (dma_dir == DMA_TO_DEVICE)
+               dir = HOST_TO_DEVICE;
+       else if (dma_dir == DMA_FROM_DEVICE)
+               dir = DEVICE_TO_HOST;
+       else
+               return -ENXIO;
+
+       if (card == SD_CARD)
+               rtsx->check_card_cd = SD_EXIST;
+       else if (card == MS_CARD)
+               rtsx->check_card_cd = MS_EXIST;
+       else if (card == XD_CARD)
+               rtsx->check_card_cd = XD_EXIST;
+       else
+               rtsx->check_card_cd = 0;
+
+       spin_lock_irq(&rtsx->reg_lock);
+
+       /* set up data structures for the wakeup system */
+       rtsx->done = &trans_done;
+
+       rtsx->trans_state = STATE_TRANS_SG;
+       rtsx->trans_result = TRANS_NOT_READY;
+
+       spin_unlock_irq(&rtsx->reg_lock);
+
+       buf_cnt = dma_map_sg(&(rtsx->pci->dev), sg, num_sg, dma_dir);
+
+       sg_ptr = sg;
+
+       for (i = 0; i <= buf_cnt / (HOST_SG_TBL_BUF_LEN / 8); i++) {
+               u32 val = TRIG_DMA;
+               int sg_cnt, j;
+
+               if (i == buf_cnt / (HOST_SG_TBL_BUF_LEN / 8))
+                       sg_cnt = buf_cnt % (HOST_SG_TBL_BUF_LEN / 8);
+               else
+                       sg_cnt = (HOST_SG_TBL_BUF_LEN / 8);
+
+               chip->sgi = 0;
+               for (j = 0; j < sg_cnt; j++) {
+                       dma_addr_t addr = sg_dma_address(sg_ptr);
+                       unsigned int len = sg_dma_len(sg_ptr);
+                       u8 option;
+
+                       RTSX_DEBUGP("DMA addr: 0x%x, Len: 0x%x\n",
+                                    (unsigned int)addr, len);
+
+                       if (j == (sg_cnt - 1))
+                               option = SG_VALID | SG_END | SG_TRANS_DATA;
+                       else
+                               option = SG_VALID | SG_TRANS_DATA;
+
+                       rtsx_add_sg_tbl(chip, (u32)addr, (u32)len, option);
+
+                       sg_ptr = sg_next(sg_ptr);
+               }
+
+               RTSX_DEBUGP("SG table count = %d\n", chip->sgi);
+
+               val |= (u32)(dir & 0x01) << 29;
+               val |= ADMA_MODE;
+
+               spin_lock_irq(&rtsx->reg_lock);
+
+               init_completion(&trans_done);
+
+               rtsx_writel(chip, RTSX_HDBAR, chip->host_sg_tbl_addr);
+               rtsx_writel(chip, RTSX_HDBCTLR, val);
+
+               spin_unlock_irq(&rtsx->reg_lock);
+
+               timeleft = wait_for_completion_interruptible_timeout(
+                       &trans_done, timeout * HZ / 1000);
+               if (timeleft <= 0) {
+                       RTSX_DEBUGP("Timeout (%s %d)\n", __func__, __LINE__);
+                       RTSX_DEBUGP("chip->int_reg = 0x%x\n", chip->int_reg);
+                       err = -ETIMEDOUT;
+                       goto out;
+               }
+
+               spin_lock_irq(&rtsx->reg_lock);
+               if (rtsx->trans_result == TRANS_RESULT_FAIL) {
+                       err = -EIO;
+                       spin_unlock_irq(&rtsx->reg_lock);
+                       goto out;
+               }
+               spin_unlock_irq(&rtsx->reg_lock);
+
+               sg_ptr += sg_cnt;
+       }
+
+       /* Wait for TRANS_OK_INT */
+       spin_lock_irq(&rtsx->reg_lock);
+       if (rtsx->trans_result == TRANS_NOT_READY) {
+               init_completion(&trans_done);
+               spin_unlock_irq(&rtsx->reg_lock);
+               timeleft = wait_for_completion_interruptible_timeout(
+                       &trans_done, timeout * HZ / 1000);
+               if (timeleft <= 0) {
+                       RTSX_DEBUGP("Timeout (%s %d)\n", __func__, __LINE__);
+                       RTSX_DEBUGP("chip->int_reg = 0x%x\n", chip->int_reg);
+                       err = -ETIMEDOUT;
+                       goto out;
+               }
+       } else {
+               spin_unlock_irq(&rtsx->reg_lock);
+       }
+
+       spin_lock_irq(&rtsx->reg_lock);
+       if (rtsx->trans_result == TRANS_RESULT_FAIL)
+               err = -EIO;
+       else if (rtsx->trans_result == TRANS_RESULT_OK)
+               err = 0;
+
+       spin_unlock_irq(&rtsx->reg_lock);
+
+out:
+       rtsx->done = NULL;
+       rtsx->trans_state = STATE_TRANS_NONE;
+       dma_unmap_sg(&(rtsx->pci->dev), sg, num_sg, dma_dir);
+
+       if (err < 0)
+               rtsx_stop_cmd(chip, card);
+
+       return err;
+}
+
+static int rtsx_transfer_buf(struct rtsx_chip *chip, u8 card, void *buf, size_t len,
+               enum dma_data_direction dma_dir, int timeout)
+{
+       struct rtsx_dev *rtsx = chip->rtsx;
+       struct completion trans_done;
+       dma_addr_t addr;
+       u8 dir;
+       int err = 0;
+       u32 val = (1 << 31);
+       long timeleft;
+
+       if ((buf == NULL) || (len <= 0))
+               return -EIO;
+
+       if (dma_dir == DMA_TO_DEVICE)
+               dir = HOST_TO_DEVICE;
+       else if (dma_dir == DMA_FROM_DEVICE)
+               dir = DEVICE_TO_HOST;
+       else
+               return -ENXIO;
+
+       addr = dma_map_single(&(rtsx->pci->dev), buf, len, dma_dir);
+       if (!addr)
+               return -ENOMEM;
+
+       if (card == SD_CARD)
+               rtsx->check_card_cd = SD_EXIST;
+       else if (card == MS_CARD)
+               rtsx->check_card_cd = MS_EXIST;
+       else if (card == XD_CARD)
+               rtsx->check_card_cd = XD_EXIST;
+       else
+               rtsx->check_card_cd = 0;
+
+       val |= (u32)(dir & 0x01) << 29;
+       val |= (u32)(len & 0x00FFFFFF);
+
+       spin_lock_irq(&rtsx->reg_lock);
+
+       /* set up data structures for the wakeup system */
+       rtsx->done = &trans_done;
+
+       init_completion(&trans_done);
+
+       rtsx->trans_state = STATE_TRANS_BUF;
+       rtsx->trans_result = TRANS_NOT_READY;
+
+       rtsx_writel(chip, RTSX_HDBAR, addr);
+       rtsx_writel(chip, RTSX_HDBCTLR, val);
+
+       spin_unlock_irq(&rtsx->reg_lock);
+
+       /* Wait for TRANS_OK_INT */
+       timeleft = wait_for_completion_interruptible_timeout(
+               &trans_done, timeout * HZ / 1000);
+       if (timeleft <= 0) {
+               RTSX_DEBUGP("Timeout (%s %d)\n", __func__, __LINE__);
+               RTSX_DEBUGP("chip->int_reg = 0x%x\n", chip->int_reg);
+               err = -ETIMEDOUT;
+               goto out;
+       }
+
+       spin_lock_irq(&rtsx->reg_lock);
+       if (rtsx->trans_result == TRANS_RESULT_FAIL)
+               err = -EIO;
+       else if (rtsx->trans_result == TRANS_RESULT_OK)
+               err = 0;
+
+       spin_unlock_irq(&rtsx->reg_lock);
+
+out:
+       rtsx->done = NULL;
+       rtsx->trans_state = STATE_TRANS_NONE;
+       dma_unmap_single(&(rtsx->pci->dev), addr, len, dma_dir);
+
+       if (err < 0)
+               rtsx_stop_cmd(chip, card);
+
+       return err;
+}
+
+int rtsx_transfer_data_partial(struct rtsx_chip *chip, u8 card,
+               void *buf, size_t len, int use_sg, unsigned int *index,
+               unsigned int *offset, enum dma_data_direction dma_dir,
+               int timeout)
+{
+       int err = 0;
+
+       /* don't transfer data during abort processing */
+       if (rtsx_chk_stat(chip, RTSX_STAT_ABORT))
+               return -EIO;
+
+       if (use_sg) {
+               err = rtsx_transfer_sglist_adma_partial(chip, card,
+                               (struct scatterlist *)buf, use_sg,
+                               index, offset, (int)len, dma_dir, timeout);
+       } else {
+               err = rtsx_transfer_buf(chip, card,
+                                       buf, len, dma_dir, timeout);
+       }
+
+       if (err < 0) {
+               if (RTSX_TST_DELINK(chip)) {
+                       RTSX_CLR_DELINK(chip);
+                       chip->need_reinit = SD_CARD | MS_CARD | XD_CARD;
+                       rtsx_reinit_cards(chip, 1);
+               }
+       }
+
+       return err;
+}
+
+int rtsx_transfer_data(struct rtsx_chip *chip, u8 card, void *buf, size_t len,
+               int use_sg, enum dma_data_direction dma_dir, int timeout)
+{
+       int err = 0;
+
+       RTSX_DEBUGP("use_sg = %d\n", use_sg);
+
+       /* don't transfer data during abort processing */
+       if (rtsx_chk_stat(chip, RTSX_STAT_ABORT))
+               return -EIO;
+
+       if (use_sg) {
+               err = rtsx_transfer_sglist_adma(chip, card,
+                               (struct scatterlist *)buf,
+                               use_sg, dma_dir, timeout);
+       } else {
+               err = rtsx_transfer_buf(chip, card, buf, len, dma_dir, timeout);
+       }
+
+       if (err < 0) {
+               if (RTSX_TST_DELINK(chip)) {
+                       RTSX_CLR_DELINK(chip);
+                       chip->need_reinit = SD_CARD | MS_CARD | XD_CARD;
+                       rtsx_reinit_cards(chip, 1);
+               }
+       }
+
+       return err;
+}
+
diff --git a/drivers/staging/rts5208/rtsx_transport.h b/drivers/staging/rts5208/rtsx_transport.h
new file mode 100644 (file)
index 0000000..b4b1123
--- /dev/null
@@ -0,0 +1,66 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_TRANSPORT_H
+#define __REALTEK_RTSX_TRANSPORT_H
+
+#include "rtsx.h"
+#include "rtsx_chip.h"
+
+#define WAIT_TIME      2000
+
+unsigned int rtsx_stor_access_xfer_buf(unsigned char *buffer,
+       unsigned int buflen, struct scsi_cmnd *srb, unsigned int *index,
+       unsigned int *offset, enum xfer_buf_dir dir);
+void rtsx_stor_set_xfer_buf(unsigned char *buffer,
+       unsigned int buflen, struct scsi_cmnd *srb);
+void rtsx_stor_get_xfer_buf(unsigned char *buffer,
+       unsigned int buflen, struct scsi_cmnd *srb);
+void rtsx_invoke_transport(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+
+
+#define rtsx_init_cmd(chip)                    ((chip)->ci = 0)
+
+void rtsx_add_cmd(struct rtsx_chip *chip,
+               u8 cmd_type, u16 reg_addr, u8 mask, u8 data);
+void rtsx_send_cmd_no_wait(struct rtsx_chip *chip);
+int rtsx_send_cmd(struct rtsx_chip *chip, u8 card, int timeout);
+
+extern inline u8 *rtsx_get_cmd_data(struct rtsx_chip *chip)
+{
+#ifdef CMD_USING_SG
+       return (u8 *)(chip->host_sg_tbl_ptr);
+#else
+       return (u8 *)(chip->host_cmds_ptr);
+#endif
+}
+
+int rtsx_transfer_data(struct rtsx_chip *chip, u8 card, void *buf, size_t len,
+               int use_sg, enum dma_data_direction dma_dir, int timeout);
+
+int rtsx_transfer_data_partial(struct rtsx_chip *chip, u8 card,
+                       void *buf, size_t len,
+                       int use_sg, unsigned int *index, unsigned int *offset,
+                       enum dma_data_direction dma_dir, int timeout);
+
+#endif   /* __REALTEK_RTSX_TRANSPORT_H */
diff --git a/drivers/staging/rts5208/sd.c b/drivers/staging/rts5208/sd.c
new file mode 100644 (file)
index 0000000..c7c1f54
--- /dev/null
@@ -0,0 +1,4525 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+
+#include "rtsx.h"
+#include "rtsx_transport.h"
+#include "rtsx_scsi.h"
+#include "rtsx_card.h"
+#include "sd.h"
+
+#define SD_MAX_RETRY_COUNT     3
+
+static u16 REG_SD_CFG1;
+static u16 REG_SD_CFG2;
+static u16 REG_SD_CFG3;
+static u16 REG_SD_STAT1;
+static u16 REG_SD_STAT2;
+static u16 REG_SD_BUS_STAT;
+static u16 REG_SD_PAD_CTL;
+static u16 REG_SD_SAMPLE_POINT_CTL;
+static u16 REG_SD_PUSH_POINT_CTL;
+static u16 REG_SD_CMD0;
+static u16 REG_SD_CMD1;
+static u16 REG_SD_CMD2;
+static u16 REG_SD_CMD3;
+static u16 REG_SD_CMD4;
+static u16 REG_SD_CMD5;
+static u16 REG_SD_BYTE_CNT_L;
+static u16 REG_SD_BYTE_CNT_H;
+static u16 REG_SD_BLOCK_CNT_L;
+static u16 REG_SD_BLOCK_CNT_H;
+static u16 REG_SD_TRANSFER;
+static u16 REG_SD_VPCLK0_CTL;
+static u16 REG_SD_VPCLK1_CTL;
+static u16 REG_SD_DCMPS0_CTL;
+static u16 REG_SD_DCMPS1_CTL;
+
+static inline void sd_set_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+
+       sd_card->err_code |= err_code;
+}
+
+static inline void sd_clr_err_code(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+
+       sd_card->err_code = 0;
+}
+
+static inline int sd_check_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+
+       return sd_card->err_code & err_code;
+}
+
+static void sd_init_reg_addr(struct rtsx_chip *chip)
+{
+       REG_SD_CFG1 = 0xFD31;
+       REG_SD_CFG2 = 0xFD33;
+       REG_SD_CFG3 = 0xFD3E;
+       REG_SD_STAT1 = 0xFD30;
+       REG_SD_STAT2 = 0;
+       REG_SD_BUS_STAT = 0;
+       REG_SD_PAD_CTL = 0;
+       REG_SD_SAMPLE_POINT_CTL = 0;
+       REG_SD_PUSH_POINT_CTL = 0;
+       REG_SD_CMD0 = 0xFD34;
+       REG_SD_CMD1 = 0xFD35;
+       REG_SD_CMD2 = 0xFD36;
+       REG_SD_CMD3 = 0xFD37;
+       REG_SD_CMD4 = 0xFD38;
+       REG_SD_CMD5 = 0xFD5A;
+       REG_SD_BYTE_CNT_L = 0xFD39;
+       REG_SD_BYTE_CNT_H = 0xFD3A;
+       REG_SD_BLOCK_CNT_L = 0xFD3B;
+       REG_SD_BLOCK_CNT_H = 0xFD3C;
+       REG_SD_TRANSFER = 0xFD32;
+       REG_SD_VPCLK0_CTL = 0;
+       REG_SD_VPCLK1_CTL = 0;
+       REG_SD_DCMPS0_CTL = 0;
+       REG_SD_DCMPS1_CTL = 0;
+}
+
+static int sd_check_data0_status(struct rtsx_chip *chip)
+{
+       u8 stat;
+
+       RTSX_READ_REG(chip, REG_SD_STAT1, &stat);
+
+       if (!(stat & SD_DAT0_STATUS)) {
+               sd_set_err_code(chip, SD_BUSY);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_send_cmd_get_rsp(struct rtsx_chip *chip, u8 cmd_idx,
+               u32 arg, u8 rsp_type, u8 *rsp, int rsp_len)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       int timeout = 100;
+       u16 reg_addr;
+       u8 *ptr;
+       int stat_idx = 0;
+       int rty_cnt = 0;
+
+       sd_clr_err_code(chip);
+
+       RTSX_DEBUGP("SD/MMC CMD %d, arg = 0x%08x\n", cmd_idx, arg);
+
+       if (rsp_type == SD_RSP_TYPE_R1b)
+               timeout = 3000;
+
+RTY_SEND_CMD:
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, 0x40 | cmd_idx);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF, (u8)(arg >> 24));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF, (u8)(arg >> 16));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF, (u8)(arg >> 8));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF, (u8)arg);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, rsp_type);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, PINGPONG_BUFFER);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER,
+                       0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+               SD_TRANSFER_END | SD_STAT_IDLE, SD_TRANSFER_END | SD_STAT_IDLE);
+
+       if (rsp_type == SD_RSP_TYPE_R2) {
+               for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16;
+                    reg_addr++)
+                       rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
+
+               stat_idx = 16;
+       } else if (rsp_type != SD_RSP_TYPE_R0) {
+               for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4;
+                    reg_addr++)
+                       rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
+
+               stat_idx = 5;
+       }
+
+       rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_STAT1, 0, 0);
+
+       retval = rtsx_send_cmd(chip, SD_CARD, timeout);
+       if (retval < 0) {
+               u8 val;
+
+               rtsx_read_register(chip, REG_SD_STAT1, &val);
+               RTSX_DEBUGP("SD_STAT1: 0x%x\n", val);
+
+               rtsx_read_register(chip, REG_SD_CFG3, &val);
+               RTSX_DEBUGP("SD_CFG3: 0x%x\n", val);
+
+               if (retval == -ETIMEDOUT) {
+                       if (rsp_type & SD_WAIT_BUSY_END) {
+                               retval = sd_check_data0_status(chip);
+                               if (retval != STATUS_SUCCESS) {
+                                       rtsx_clear_sd_error(chip);
+                                       TRACE_RET(chip, retval);
+                               }
+                       } else {
+                               sd_set_err_code(chip, SD_TO_ERR);
+                       }
+                       retval = STATUS_TIMEDOUT;
+               } else {
+                       retval = STATUS_FAIL;
+               }
+               rtsx_clear_sd_error(chip);
+
+               TRACE_RET(chip, retval);
+       }
+
+       if (rsp_type == SD_RSP_TYPE_R0)
+               return STATUS_SUCCESS;
+
+       ptr = rtsx_get_cmd_data(chip) + 1;
+
+       if ((ptr[0] & 0xC0) != 0) {
+               sd_set_err_code(chip, SD_STS_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (!(rsp_type & SD_NO_CHECK_CRC7)) {
+               if (ptr[stat_idx] & SD_CRC7_ERR) {
+                       if (cmd_idx == WRITE_MULTIPLE_BLOCK) {
+                               sd_set_err_code(chip, SD_CRC_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       if (rty_cnt < SD_MAX_RETRY_COUNT) {
+                               wait_timeout(20);
+                               rty_cnt++;
+                               goto RTY_SEND_CMD;
+                       } else {
+                               sd_set_err_code(chip, SD_CRC_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+       }
+
+       if ((rsp_type == SD_RSP_TYPE_R1) || (rsp_type == SD_RSP_TYPE_R1b)) {
+               if ((cmd_idx != SEND_RELATIVE_ADDR) &&
+                       (cmd_idx != SEND_IF_COND)) {
+                       if (cmd_idx != STOP_TRANSMISSION) {
+                               if (ptr[1] & 0x80)
+                                       TRACE_RET(chip, STATUS_FAIL);
+                       }
+#ifdef SUPPORT_SD_LOCK
+                       if (ptr[1] & 0x7D)
+#else
+                       if (ptr[1] & 0x7F)
+#endif
+                       {
+                               RTSX_DEBUGP("ptr[1]: 0x%02x\n", ptr[1]);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       if (ptr[2] & 0xFF) {
+                               RTSX_DEBUGP("ptr[2]: 0x%02x\n", ptr[2]);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       if (ptr[3] & 0x80) {
+                               RTSX_DEBUGP("ptr[3]: 0x%02x\n", ptr[3]);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       if (ptr[3] & 0x01)
+                               sd_card->sd_data_buf_ready = 1;
+                       else
+                               sd_card->sd_data_buf_ready = 0;
+               }
+       }
+
+       if (rsp && rsp_len)
+               memcpy(rsp, ptr, rsp_len);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_read_data(struct rtsx_chip *chip,
+                       u8 trans_mode, u8 *cmd, int cmd_len, u16 byte_cnt,
+                       u16 blk_cnt, u8 bus_width, u8 *buf, int buf_len,
+                       int timeout)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       int i;
+
+       sd_clr_err_code(chip);
+
+       if (!buf)
+               buf_len = 0;
+
+       if (buf_len > 512)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       rtsx_init_cmd(chip);
+
+       if (cmd_len) {
+               RTSX_DEBUGP("SD/MMC CMD %d\n", cmd[0] - 0x40);
+               for (i = 0; i < (cmd_len < 6 ? cmd_len : 6); i++)
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0 + i,
+                                    0xFF, cmd[i]);
+       }
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF,
+               (u8)byte_cnt);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF,
+               (u8)(byte_cnt >> 8));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF,
+               (u8)blk_cnt);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF,
+               (u8)(blk_cnt >> 8));
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, 0x03, bus_width);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
+               SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END|
+               SD_CHECK_CRC7 | SD_RSP_LEN_6);
+       if (trans_mode != SD_TM_AUTO_TUNING)
+               rtsx_add_cmd(chip, WRITE_REG_CMD,
+                       CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+               trans_mode | SD_TRANSFER_START);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END,
+               SD_TRANSFER_END);
+
+       retval = rtsx_send_cmd(chip, SD_CARD, timeout);
+       if (retval < 0) {
+               if (retval == -ETIMEDOUT) {
+                       sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+                                           SD_RSP_TYPE_R1, NULL, 0);
+               }
+
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (buf && buf_len) {
+               retval = rtsx_read_ppbuf(chip, buf, buf_len);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_write_data(struct rtsx_chip *chip, u8 trans_mode,
+               u8 *cmd, int cmd_len, u16 byte_cnt, u16 blk_cnt, u8 bus_width,
+               u8 *buf, int buf_len, int timeout)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       int i;
+
+       sd_clr_err_code(chip);
+
+       if (!buf)
+               buf_len = 0;
+
+       if (buf_len > 512) {
+               /* This function can't write data more than one page */
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (buf && buf_len) {
+               retval = rtsx_write_ppbuf(chip, buf, buf_len);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       rtsx_init_cmd(chip);
+
+       if (cmd_len) {
+               RTSX_DEBUGP("SD/MMC CMD %d\n", cmd[0] - 0x40);
+               for (i = 0; i < (cmd_len < 6 ? cmd_len : 6); i++) {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD,
+                                    REG_SD_CMD0 + i, 0xFF, cmd[i]);
+               }
+       }
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF,
+               (u8)byte_cnt);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF,
+               (u8)(byte_cnt >> 8));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF,
+               (u8)blk_cnt);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF,
+               (u8)(blk_cnt >> 8));
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, 0x03, bus_width);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
+               SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END |
+               SD_CHECK_CRC7 | SD_RSP_LEN_6);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+               trans_mode | SD_TRANSFER_START);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END,
+               SD_TRANSFER_END);
+
+       retval = rtsx_send_cmd(chip, SD_CARD, timeout);
+       if (retval < 0) {
+               if (retval == -ETIMEDOUT) {
+                       sd_send_cmd_get_rsp(chip, SEND_STATUS,
+                               sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
+               }
+
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_check_csd(struct rtsx_chip *chip, char check_wp)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       int i;
+       u8 csd_ver, trans_speed;
+       u8 rsp[16];
+
+       for (i = 0; i < 6; i++) {
+               if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+                       sd_set_err_code(chip, SD_NO_CARD);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               retval = sd_send_cmd_get_rsp(chip, SEND_CSD, sd_card->sd_addr,
+                                       SD_RSP_TYPE_R2, rsp, 16);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+
+       if (i == 6)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       memcpy(sd_card->raw_csd, rsp + 1, 15);
+
+       RTSX_DEBUGP("CSD Response:\n");
+       RTSX_DUMP(sd_card->raw_csd, 16);
+
+       csd_ver = (rsp[1] & 0xc0) >> 6;
+       RTSX_DEBUGP("csd_ver = %d\n", csd_ver);
+
+       trans_speed = rsp[4];
+       if ((trans_speed & 0x07) == 0x02) {
+               if ((trans_speed & 0xf8) >= 0x30) {
+                       if (chip->asic_code)
+                               sd_card->sd_clock = 47;
+                       else
+                               sd_card->sd_clock = CLK_50;
+
+               } else if ((trans_speed & 0xf8) == 0x28) {
+                       if (chip->asic_code)
+                               sd_card->sd_clock = 39;
+                       else
+                               sd_card->sd_clock = CLK_40;
+
+               } else if ((trans_speed & 0xf8) == 0x20) {
+                       if (chip->asic_code)
+                               sd_card->sd_clock = 29;
+                       else
+                               sd_card->sd_clock = CLK_30;
+
+               } else if ((trans_speed & 0xf8) >= 0x10) {
+                       if (chip->asic_code)
+                               sd_card->sd_clock = 23;
+                       else
+                               sd_card->sd_clock = CLK_20;
+
+               } else if ((trans_speed & 0x08) >= 0x08) {
+                       if (chip->asic_code)
+                               sd_card->sd_clock = 19;
+                       else
+                               sd_card->sd_clock = CLK_20;
+               } else {
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       } else {
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (CHK_MMC_SECTOR_MODE(sd_card)) {
+               sd_card->capacity = 0;
+       } else {
+               if ((!CHK_SD_HCXC(sd_card)) || (csd_ver == 0)) {
+                       u8 blk_size, c_size_mult;
+                       u16 c_size;
+                       blk_size = rsp[6] & 0x0F;
+                       c_size =  ((u16)(rsp[7] & 0x03) << 10)
+                                       + ((u16)rsp[8] << 2)
+                                       + ((u16)(rsp[9] & 0xC0) >> 6);
+                       c_size_mult = (u8)((rsp[10] & 0x03) << 1);
+                       c_size_mult += (rsp[11] & 0x80) >> 7;
+                       sd_card->capacity = (((u32)(c_size + 1)) *
+                                       (1 << (c_size_mult + 2)))
+                               << (blk_size - 9);
+               } else {
+                       u32 total_sector = 0;
+                       total_sector = (((u32)rsp[8] & 0x3f) << 16) |
+                               ((u32)rsp[9] << 8) | (u32)rsp[10];
+                       sd_card->capacity = (total_sector + 1) << 10;
+               }
+       }
+
+       if (check_wp) {
+               if (rsp[15] & 0x30)
+                       chip->card_wp |= SD_CARD;
+
+               RTSX_DEBUGP("CSD WP Status: 0x%x\n", rsp[15]);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_set_sample_push_timing(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+
+       u8 val = 0;
+
+       if ((chip->sd_ctl & SD_PUSH_POINT_CTL_MASK) == SD_PUSH_POINT_DELAY)
+               val |= 0x10;
+
+       if ((chip->sd_ctl & SD_SAMPLE_POINT_CTL_MASK) == SD_SAMPLE_POINT_AUTO) {
+               if (chip->asic_code) {
+                       if (CHK_SD_HS(sd_card) || CHK_MMC_52M(sd_card)) {
+                               if (val & 0x10)
+                                       val |= 0x04;
+                               else
+                                       val |= 0x08;
+                       }
+               } else {
+                       if (val & 0x10)
+                               val |= 0x04;
+                       else
+                               val |= 0x08;
+               }
+       } else if ((chip->sd_ctl & SD_SAMPLE_POINT_CTL_MASK) ==
+               SD_SAMPLE_POINT_DELAY) {
+               if (val & 0x10)
+                       val |= 0x04;
+               else
+                       val |= 0x08;
+       }
+
+       RTSX_WRITE_REG(chip, REG_SD_CFG1, 0x1C, val);
+
+       return STATUS_SUCCESS;
+}
+
+static void sd_choose_proper_clock(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+
+       if (CHK_SD_SDR104(sd_card)) {
+               if (chip->asic_code)
+                       sd_card->sd_clock = chip->asic_sd_sdr104_clk;
+               else
+                       sd_card->sd_clock = chip->fpga_sd_sdr104_clk;
+
+       } else if (CHK_SD_DDR50(sd_card)) {
+               if (chip->asic_code)
+                       sd_card->sd_clock = chip->asic_sd_ddr50_clk;
+               else
+                       sd_card->sd_clock = chip->fpga_sd_ddr50_clk;
+
+       } else if (CHK_SD_SDR50(sd_card)) {
+               if (chip->asic_code)
+                       sd_card->sd_clock = chip->asic_sd_sdr50_clk;
+               else
+                       sd_card->sd_clock = chip->fpga_sd_sdr50_clk;
+
+       } else if (CHK_SD_HS(sd_card)) {
+               if (chip->asic_code)
+                       sd_card->sd_clock = chip->asic_sd_hs_clk;
+               else
+                       sd_card->sd_clock = chip->fpga_sd_hs_clk;
+
+       } else if (CHK_MMC_52M(sd_card) || CHK_MMC_DDR52(sd_card)) {
+               if (chip->asic_code)
+                       sd_card->sd_clock = chip->asic_mmc_52m_clk;
+               else
+                       sd_card->sd_clock = chip->fpga_mmc_52m_clk;
+
+       } else if (CHK_MMC_26M(sd_card)) {
+               if (chip->asic_code)
+                       sd_card->sd_clock = 48;
+               else
+                       sd_card->sd_clock = CLK_50;
+       }
+}
+
+static int sd_set_clock_divider(struct rtsx_chip *chip, u8 clk_div)
+{
+       u8 mask = 0, val = 0;
+
+       mask = 0x60;
+       if (clk_div == SD_CLK_DIVIDE_0)
+               val = 0x00;
+       else if (clk_div == SD_CLK_DIVIDE_128)
+               val = 0x40;
+       else if (clk_div == SD_CLK_DIVIDE_256)
+               val = 0x20;
+
+       RTSX_WRITE_REG(chip, REG_SD_CFG1, mask, val);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_set_init_para(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+
+       retval = sd_set_sample_push_timing(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       sd_choose_proper_clock(chip);
+
+       retval = switch_clock(chip, sd_card->sd_clock);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+int sd_select_card(struct rtsx_chip *chip, int select)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       u8 cmd_idx, cmd_type;
+       u32 addr;
+
+       if (select) {
+               cmd_idx = SELECT_CARD;
+               cmd_type = SD_RSP_TYPE_R1;
+               addr = sd_card->sd_addr;
+       } else {
+               cmd_idx = DESELECT_CARD;
+               cmd_type = SD_RSP_TYPE_R0;
+               addr = 0;
+       }
+
+       retval = sd_send_cmd_get_rsp(chip, cmd_idx, addr, cmd_type, NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+#ifdef SUPPORT_SD_LOCK
+static int sd_update_lock_status(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       u8 rsp[5];
+
+       retval = sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+                               SD_RSP_TYPE_R1, rsp, 5);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (rsp[1] & 0x02)
+               sd_card->sd_lock_status |= SD_LOCKED;
+       else
+               sd_card->sd_lock_status &= ~SD_LOCKED;
+
+       RTSX_DEBUGP("sd_card->sd_lock_status = 0x%x\n",
+               sd_card->sd_lock_status);
+
+       if (rsp[1] & 0x01)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+#endif
+
+static int sd_wait_state_data_ready(struct rtsx_chip *chip, u8 state,
+                               u8 data_ready, int polling_cnt)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval, i;
+       u8 rsp[5];
+
+       for (i = 0; i < polling_cnt; i++) {
+               retval = sd_send_cmd_get_rsp(chip, SEND_STATUS,
+                                       sd_card->sd_addr, SD_RSP_TYPE_R1, rsp,
+                                       5);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if (((rsp[3] & 0x1E) == state) &&
+                       ((rsp[3] & 0x01) == data_ready))
+                       return STATUS_SUCCESS;
+       }
+
+       TRACE_RET(chip, STATUS_FAIL);
+}
+
+static int sd_change_bank_voltage(struct rtsx_chip *chip, u8 voltage)
+{
+       int retval;
+
+       if (voltage == SD_IO_3V3) {
+               if (chip->asic_code) {
+                       retval = rtsx_write_phy_register(chip, 0x08,
+                                                       0x4FC0 |
+                                                       chip->phy_voltage);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+               } else {
+                       RTSX_WRITE_REG(chip, SD_PAD_CTL, SD_IO_USING_1V8, 0);
+               }
+       } else if (voltage == SD_IO_1V8) {
+               if (chip->asic_code) {
+                       retval = rtsx_write_phy_register(chip, 0x08,
+                                                       0x4C40 |
+                                                       chip->phy_voltage);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+               } else {
+                       RTSX_WRITE_REG(chip, SD_PAD_CTL, SD_IO_USING_1V8,
+                               SD_IO_USING_1V8);
+               }
+       } else {
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_voltage_switch(struct rtsx_chip *chip)
+{
+       int retval;
+       u8 stat;
+
+       RTSX_WRITE_REG(chip, SD_BUS_STAT, SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP,
+               SD_CLK_TOGGLE_EN);
+
+       retval = sd_send_cmd_get_rsp(chip, VOLTAGE_SWITCH, 0, SD_RSP_TYPE_R1,
+                               NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       udelay(chip->sd_voltage_switch_delay);
+
+       RTSX_READ_REG(chip, SD_BUS_STAT, &stat);
+       if (stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
+                               SD_DAT1_STATUS | SD_DAT0_STATUS)) {
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       RTSX_WRITE_REG(chip, SD_BUS_STAT, 0xFF, SD_CLK_FORCE_STOP);
+       retval = sd_change_bank_voltage(chip, SD_IO_1V8);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       wait_timeout(50);
+
+       RTSX_WRITE_REG(chip, SD_BUS_STAT, 0xFF, SD_CLK_TOGGLE_EN);
+       wait_timeout(10);
+
+       RTSX_READ_REG(chip, SD_BUS_STAT, &stat);
+       if ((stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
+                               SD_DAT1_STATUS | SD_DAT0_STATUS)) !=
+                       (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
+                               SD_DAT1_STATUS | SD_DAT0_STATUS)) {
+               RTSX_DEBUGP("SD_BUS_STAT: 0x%x\n", stat);
+               rtsx_write_register(chip, SD_BUS_STAT,
+                               SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+               rtsx_write_register(chip, CARD_CLK_EN, 0xFF, 0);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       RTSX_WRITE_REG(chip, SD_BUS_STAT, SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP,
+               0);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_reset_dcm(struct rtsx_chip *chip, u8 tune_dir)
+{
+       if (tune_dir == TUNE_RX) {
+               RTSX_WRITE_REG(chip, DCM_DRP_CTL, 0xFF, DCM_RESET | DCM_RX);
+               RTSX_WRITE_REG(chip, DCM_DRP_CTL, 0xFF, DCM_RX);
+       } else {
+               RTSX_WRITE_REG(chip, DCM_DRP_CTL, 0xFF, DCM_RESET | DCM_TX);
+               RTSX_WRITE_REG(chip, DCM_DRP_CTL, 0xFF, DCM_TX);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_change_phase(struct rtsx_chip *chip, u8 sample_point, u8 tune_dir)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       u16 SD_VP_CTL, SD_DCMPS_CTL;
+       u8 val;
+       int retval;
+       int ddr_rx = 0;
+
+       RTSX_DEBUGP("sd_change_phase (sample_point = %d, tune_dir = %d)\n",
+                               sample_point, tune_dir);
+
+       if (tune_dir == TUNE_RX) {
+               SD_VP_CTL = SD_VPRX_CTL;
+               SD_DCMPS_CTL = SD_DCMPS_RX_CTL;
+               if (CHK_SD_DDR50(sd_card))
+                       ddr_rx = 1;
+       } else {
+               SD_VP_CTL = SD_VPTX_CTL;
+               SD_DCMPS_CTL = SD_DCMPS_TX_CTL;
+       }
+
+       if (chip->asic_code) {
+               RTSX_WRITE_REG(chip, CLK_CTL, CHANGE_CLK, CHANGE_CLK);
+               RTSX_WRITE_REG(chip, SD_VP_CTL, 0x1F, sample_point);
+               RTSX_WRITE_REG(chip, SD_VPCLK0_CTL, PHASE_NOT_RESET, 0);
+               RTSX_WRITE_REG(chip, SD_VPCLK0_CTL, PHASE_NOT_RESET,
+                       PHASE_NOT_RESET);
+               RTSX_WRITE_REG(chip, CLK_CTL, CHANGE_CLK, 0);
+       } else {
+#ifdef CONFIG_RTS5208_DEBUG
+               rtsx_read_register(chip, SD_VP_CTL, &val);
+               RTSX_DEBUGP("SD_VP_CTL: 0x%x\n", val);
+               rtsx_read_register(chip, SD_DCMPS_CTL, &val);
+               RTSX_DEBUGP("SD_DCMPS_CTL: 0x%x\n", val);
+#endif
+
+               if (ddr_rx) {
+                       RTSX_WRITE_REG(chip, SD_VP_CTL, PHASE_CHANGE,
+                               PHASE_CHANGE);
+                       udelay(50);
+                       RTSX_WRITE_REG(chip, SD_VP_CTL, 0xFF,
+                               PHASE_CHANGE | PHASE_NOT_RESET | sample_point);
+               } else {
+                       RTSX_WRITE_REG(chip, CLK_CTL, CHANGE_CLK, CHANGE_CLK);
+                       udelay(50);
+                       RTSX_WRITE_REG(chip, SD_VP_CTL, 0xFF,
+                                       PHASE_NOT_RESET | sample_point);
+               }
+               udelay(100);
+
+               rtsx_init_cmd(chip);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SD_DCMPS_CTL, DCMPS_CHANGE,
+                       DCMPS_CHANGE);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, SD_DCMPS_CTL,
+                       DCMPS_CHANGE_DONE, DCMPS_CHANGE_DONE);
+               retval = rtsx_send_cmd(chip, SD_CARD, 100);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, Fail);
+
+               val = *rtsx_get_cmd_data(chip);
+               if (val & DCMPS_ERROR)
+                       TRACE_GOTO(chip, Fail);
+
+               if ((val & DCMPS_CURRENT_PHASE) != sample_point)
+                       TRACE_GOTO(chip, Fail);
+
+               RTSX_WRITE_REG(chip, SD_DCMPS_CTL, DCMPS_CHANGE, 0);
+               if (ddr_rx)
+                       RTSX_WRITE_REG(chip, SD_VP_CTL, PHASE_CHANGE, 0);
+               else
+                       RTSX_WRITE_REG(chip, CLK_CTL, CHANGE_CLK, 0);
+
+               udelay(50);
+       }
+
+       RTSX_WRITE_REG(chip, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0);
+
+       return STATUS_SUCCESS;
+
+Fail:
+#ifdef CONFIG_RTS5208_DEBUG
+       rtsx_read_register(chip, SD_VP_CTL, &val);
+       RTSX_DEBUGP("SD_VP_CTL: 0x%x\n", val);
+       rtsx_read_register(chip, SD_DCMPS_CTL, &val);
+       RTSX_DEBUGP("SD_DCMPS_CTL: 0x%x\n", val);
+#endif
+
+       rtsx_write_register(chip, SD_DCMPS_CTL, DCMPS_CHANGE, 0);
+       rtsx_write_register(chip, SD_VP_CTL, PHASE_CHANGE, 0);
+       wait_timeout(10);
+       sd_reset_dcm(chip, tune_dir);
+       return STATUS_FAIL;
+}
+
+static int sd_check_spec(struct rtsx_chip *chip, u8 bus_width)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       u8 cmd[5], buf[8];
+
+       retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
+                               SD_RSP_TYPE_R1, NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       cmd[0] = 0x40 | SEND_SCR;
+       cmd[1] = 0;
+       cmd[2] = 0;
+       cmd[3] = 0;
+       cmd[4] = 0;
+
+       retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, 8, 1, bus_width,
+                       buf, 8, 250);
+       if (retval != STATUS_SUCCESS) {
+               rtsx_clear_sd_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       memcpy(sd_card->raw_scr, buf, 8);
+
+       if ((buf[0] & 0x0F) == 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_query_switch_result(struct rtsx_chip *chip, u8 func_group,
+                               u8 func_to_switch, u8 *buf, int buf_len)
+{
+       u8 support_mask = 0, query_switch = 0, switch_busy = 0;
+       int support_offset = 0, query_switch_offset = 0, check_busy_offset = 0;
+
+       if (func_group == SD_FUNC_GROUP_1) {
+               support_offset = FUNCTION_GROUP1_SUPPORT_OFFSET;
+               query_switch_offset = FUNCTION_GROUP1_QUERY_SWITCH_OFFSET;
+               check_busy_offset = FUNCTION_GROUP1_CHECK_BUSY_OFFSET;
+
+               switch (func_to_switch) {
+               case HS_SUPPORT:
+                       support_mask = HS_SUPPORT_MASK;
+                       query_switch = HS_QUERY_SWITCH_OK;
+                       switch_busy = HS_SWITCH_BUSY;
+                       break;
+
+               case SDR50_SUPPORT:
+                       support_mask = SDR50_SUPPORT_MASK;
+                       query_switch = SDR50_QUERY_SWITCH_OK;
+                       switch_busy = SDR50_SWITCH_BUSY;
+                       break;
+
+               case SDR104_SUPPORT:
+                       support_mask = SDR104_SUPPORT_MASK;
+                       query_switch = SDR104_QUERY_SWITCH_OK;
+                       switch_busy = SDR104_SWITCH_BUSY;
+                       break;
+
+               case DDR50_SUPPORT:
+                       support_mask = DDR50_SUPPORT_MASK;
+                       query_switch = DDR50_QUERY_SWITCH_OK;
+                       switch_busy = DDR50_SWITCH_BUSY;
+                       break;
+
+               default:
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       } else if (func_group == SD_FUNC_GROUP_3) {
+               support_offset = FUNCTION_GROUP3_SUPPORT_OFFSET;
+               query_switch_offset = FUNCTION_GROUP3_QUERY_SWITCH_OFFSET;
+               check_busy_offset = FUNCTION_GROUP3_CHECK_BUSY_OFFSET;
+
+               switch (func_to_switch) {
+               case DRIVING_TYPE_A:
+                       support_mask = DRIVING_TYPE_A_MASK;
+                       query_switch = TYPE_A_QUERY_SWITCH_OK;
+                       switch_busy = TYPE_A_SWITCH_BUSY;
+                       break;
+
+               case DRIVING_TYPE_C:
+                       support_mask = DRIVING_TYPE_C_MASK;
+                       query_switch = TYPE_C_QUERY_SWITCH_OK;
+                       switch_busy = TYPE_C_SWITCH_BUSY;
+                       break;
+
+               case DRIVING_TYPE_D:
+                       support_mask = DRIVING_TYPE_D_MASK;
+                       query_switch = TYPE_D_QUERY_SWITCH_OK;
+                       switch_busy = TYPE_D_SWITCH_BUSY;
+                       break;
+
+               default:
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       } else if (func_group == SD_FUNC_GROUP_4) {
+               support_offset = FUNCTION_GROUP4_SUPPORT_OFFSET;
+               query_switch_offset = FUNCTION_GROUP4_QUERY_SWITCH_OFFSET;
+               check_busy_offset = FUNCTION_GROUP4_CHECK_BUSY_OFFSET;
+
+               switch (func_to_switch) {
+               case CURRENT_LIMIT_400:
+                       support_mask = CURRENT_LIMIT_400_MASK;
+                       query_switch = CURRENT_LIMIT_400_QUERY_SWITCH_OK;
+                       switch_busy = CURRENT_LIMIT_400_SWITCH_BUSY;
+                       break;
+
+               case CURRENT_LIMIT_600:
+                       support_mask = CURRENT_LIMIT_600_MASK;
+                       query_switch = CURRENT_LIMIT_600_QUERY_SWITCH_OK;
+                       switch_busy = CURRENT_LIMIT_600_SWITCH_BUSY;
+                       break;
+
+               case CURRENT_LIMIT_800:
+                       support_mask = CURRENT_LIMIT_800_MASK;
+                       query_switch = CURRENT_LIMIT_800_QUERY_SWITCH_OK;
+                       switch_busy = CURRENT_LIMIT_800_SWITCH_BUSY;
+                       break;
+
+               default:
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       } else {
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (func_group == SD_FUNC_GROUP_1) {
+               if (!(buf[support_offset] & support_mask) ||
+                       ((buf[query_switch_offset] & 0x0F) != query_switch)) {
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       /* Check 'Busy Status' */
+       if ((buf[DATA_STRUCTURE_VER_OFFSET] == 0x01) &&
+                   ((buf[check_busy_offset] & switch_busy) == switch_busy)) {
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_check_switch_mode(struct rtsx_chip *chip, u8 mode,
+               u8 func_group, u8 func_to_switch, u8 bus_width)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       u8 cmd[5], buf[64];
+
+       RTSX_DEBUGP("sd_check_switch_mode (mode = %d, func_group = %d, func_to_switch = %d)\n",
+                       mode, func_group, func_to_switch);
+
+       cmd[0] = 0x40 | SWITCH;
+       cmd[1] = mode;
+
+       if (func_group == SD_FUNC_GROUP_1) {
+               cmd[2] = 0xFF;
+               cmd[3] = 0xFF;
+               cmd[4] = 0xF0 + func_to_switch;
+       } else if (func_group == SD_FUNC_GROUP_3) {
+               cmd[2] = 0xFF;
+               cmd[3] = 0xF0 + func_to_switch;
+               cmd[4] = 0xFF;
+       } else if (func_group == SD_FUNC_GROUP_4) {
+               cmd[2] = 0xFF;
+               cmd[3] = 0x0F + (func_to_switch << 4);
+               cmd[4] = 0xFF;
+       } else {
+               cmd[1] = SD_CHECK_MODE;
+               cmd[2] = 0xFF;
+               cmd[3] = 0xFF;
+               cmd[4] = 0xFF;
+       }
+
+       retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, 64, 1, bus_width,
+                       buf, 64, 250);
+       if (retval != STATUS_SUCCESS) {
+               rtsx_clear_sd_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       RTSX_DUMP(buf, 64);
+
+       if (func_group == NO_ARGUMENT) {
+               sd_card->func_group1_mask = buf[0x0D];
+               sd_card->func_group2_mask = buf[0x0B];
+               sd_card->func_group3_mask = buf[0x09];
+               sd_card->func_group4_mask = buf[0x07];
+
+               RTSX_DEBUGP("func_group1_mask = 0x%02x\n", buf[0x0D]);
+               RTSX_DEBUGP("func_group2_mask = 0x%02x\n", buf[0x0B]);
+               RTSX_DEBUGP("func_group3_mask = 0x%02x\n", buf[0x09]);
+               RTSX_DEBUGP("func_group4_mask = 0x%02x\n", buf[0x07]);
+       } else {
+               /* Maximum current consumption, check whether current is
+                * acceptable; bit[511:496] = 0x0000 means some error happened.
+                */
+               u16 cc = ((u16)buf[0] << 8) | buf[1];
+               RTSX_DEBUGP("Maximum current consumption: %dmA\n", cc);
+               if ((cc == 0) || (cc > 800))
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = sd_query_switch_result(chip, func_group,
+                                               func_to_switch, buf, 64);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if ((cc > 400) || (func_to_switch > CURRENT_LIMIT_400)) {
+                       RTSX_WRITE_REG(chip, OCPPARA2, SD_OCP_THD_MASK,
+                               chip->sd_800mA_ocp_thd);
+                       RTSX_WRITE_REG(chip, CARD_PWR_CTL, PMOS_STRG_MASK,
+                               PMOS_STRG_800mA);
+               }
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static u8 downgrade_switch_mode(u8 func_group, u8 func_to_switch)
+{
+       if (func_group == SD_FUNC_GROUP_1) {
+               if (func_to_switch > HS_SUPPORT)
+                       func_to_switch--;
+
+       } else if (func_group == SD_FUNC_GROUP_4) {
+               if (func_to_switch > CURRENT_LIMIT_200)
+                       func_to_switch--;
+       }
+
+       return func_to_switch;
+}
+
+static int sd_check_switch(struct rtsx_chip *chip,
+               u8 func_group, u8 func_to_switch, u8 bus_width)
+{
+       int retval;
+       int i;
+       int switch_good = 0;
+
+       for (i = 0; i < 3; i++) {
+               if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+                       sd_set_err_code(chip, SD_NO_CARD);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               retval = sd_check_switch_mode(chip, SD_CHECK_MODE, func_group,
+                               func_to_switch, bus_width);
+               if (retval == STATUS_SUCCESS) {
+                       u8 stat;
+
+                       retval = sd_check_switch_mode(chip, SD_SWITCH_MODE,
+                                       func_group, func_to_switch, bus_width);
+                       if (retval == STATUS_SUCCESS) {
+                               switch_good = 1;
+                               break;
+                       }
+
+                       RTSX_READ_REG(chip, SD_STAT1, &stat);
+                       if (stat & SD_CRC16_ERR) {
+                               RTSX_DEBUGP("SD CRC16 error when switching mode\n");
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+
+               func_to_switch = downgrade_switch_mode(func_group,
+                                               func_to_switch);
+
+               wait_timeout(20);
+       }
+
+       if (!switch_good)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_switch_function(struct rtsx_chip *chip, u8 bus_width)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       int i;
+       u8 func_to_switch = 0;
+
+       /* Get supported functions */
+       retval = sd_check_switch_mode(chip, SD_CHECK_MODE,
+                       NO_ARGUMENT, NO_ARGUMENT, bus_width);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       sd_card->func_group1_mask &= ~(sd_card->sd_switch_fail);
+
+       /* Function Group 1: Access Mode */
+       for (i = 0; i < 4; i++) {
+               switch ((u8)(chip->sd_speed_prior >> (i*8))) {
+               case SDR104_SUPPORT:
+                       if ((sd_card->func_group1_mask & SDR104_SUPPORT_MASK)
+                                       && chip->sdr104_en) {
+                               func_to_switch = SDR104_SUPPORT;
+                       }
+                       break;
+
+               case DDR50_SUPPORT:
+                       if ((sd_card->func_group1_mask & DDR50_SUPPORT_MASK)
+                                       && chip->ddr50_en) {
+                               func_to_switch = DDR50_SUPPORT;
+                       }
+                       break;
+
+               case SDR50_SUPPORT:
+                       if ((sd_card->func_group1_mask & SDR50_SUPPORT_MASK)
+                                       && chip->sdr50_en) {
+                               func_to_switch = SDR50_SUPPORT;
+                       }
+                       break;
+
+               case HS_SUPPORT:
+                       if (sd_card->func_group1_mask & HS_SUPPORT_MASK)
+                               func_to_switch = HS_SUPPORT;
+
+                       break;
+
+               default:
+                       continue;
+               }
+
+
+               if (func_to_switch)
+                       break;
+
+       }
+       RTSX_DEBUGP("SD_FUNC_GROUP_1: func_to_switch = 0x%02x", func_to_switch);
+
+#ifdef SUPPORT_SD_LOCK
+       if ((sd_card->sd_lock_status & SD_SDR_RST)
+                       && (DDR50_SUPPORT == func_to_switch)
+                       && (sd_card->func_group1_mask & SDR50_SUPPORT_MASK)) {
+               func_to_switch = SDR50_SUPPORT;
+               RTSX_DEBUGP("Using SDR50 instead of DDR50 for SD Lock\n");
+       }
+#endif
+
+       if (func_to_switch) {
+               retval = sd_check_switch(chip, SD_FUNC_GROUP_1, func_to_switch,
+                                       bus_width);
+               if (retval != STATUS_SUCCESS) {
+                       if (func_to_switch == SDR104_SUPPORT) {
+                               sd_card->sd_switch_fail = SDR104_SUPPORT_MASK;
+                       } else if (func_to_switch == DDR50_SUPPORT) {
+                               sd_card->sd_switch_fail = SDR104_SUPPORT_MASK |
+                                       DDR50_SUPPORT_MASK;
+                       } else if (func_to_switch == SDR50_SUPPORT) {
+                               sd_card->sd_switch_fail = SDR104_SUPPORT_MASK |
+                                       DDR50_SUPPORT_MASK | SDR50_SUPPORT_MASK;
+                       }
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               if (func_to_switch == SDR104_SUPPORT)
+                       SET_SD_SDR104(sd_card);
+               else if (func_to_switch == DDR50_SUPPORT)
+                       SET_SD_DDR50(sd_card);
+               else if (func_to_switch == SDR50_SUPPORT)
+                       SET_SD_SDR50(sd_card);
+               else
+                       SET_SD_HS(sd_card);
+       }
+
+       if (CHK_SD_DDR50(sd_card)) {
+               RTSX_WRITE_REG(chip, SD_PUSH_POINT_CTL, 0x06, 0x04);
+               retval = sd_set_sample_push_timing(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (!func_to_switch || (func_to_switch == HS_SUPPORT)) {
+               /* Do not try to switch current limit if the card doesn't
+                * support UHS mode or we don't want it to support UHS mode
+                */
+               return STATUS_SUCCESS;
+       }
+
+       /* Function Group 4: Current Limit */
+       func_to_switch = 0xFF;
+
+       for (i = 0; i < 4; i++) {
+               switch ((u8)(chip->sd_current_prior >> (i*8))) {
+               case CURRENT_LIMIT_800:
+                       if (sd_card->func_group4_mask & CURRENT_LIMIT_800_MASK)
+                               func_to_switch = CURRENT_LIMIT_800;
+
+                       break;
+
+               case CURRENT_LIMIT_600:
+                       if (sd_card->func_group4_mask & CURRENT_LIMIT_600_MASK)
+                               func_to_switch = CURRENT_LIMIT_600;
+
+                       break;
+
+               case CURRENT_LIMIT_400:
+                       if (sd_card->func_group4_mask & CURRENT_LIMIT_400_MASK)
+                               func_to_switch = CURRENT_LIMIT_400;
+
+                       break;
+
+               case CURRENT_LIMIT_200:
+                       if (sd_card->func_group4_mask & CURRENT_LIMIT_200_MASK)
+                               func_to_switch = CURRENT_LIMIT_200;
+
+                       break;
+
+               default:
+                       continue;
+               }
+
+               if (func_to_switch != 0xFF)
+                       break;
+       }
+
+       RTSX_DEBUGP("SD_FUNC_GROUP_4: func_to_switch = 0x%02x", func_to_switch);
+
+       if (func_to_switch <= CURRENT_LIMIT_800) {
+               retval = sd_check_switch(chip, SD_FUNC_GROUP_4, func_to_switch,
+                                       bus_width);
+               if (retval != STATUS_SUCCESS) {
+                       if (sd_check_err_code(chip, SD_NO_CARD))
+                               TRACE_RET(chip, STATUS_FAIL);
+               }
+               RTSX_DEBUGP("Switch current limit finished! (%d)\n", retval);
+       }
+
+       if (CHK_SD_DDR50(sd_card))
+               RTSX_WRITE_REG(chip, SD_PUSH_POINT_CTL, 0x06, 0);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_wait_data_idle(struct rtsx_chip *chip)
+{
+       int retval = STATUS_TIMEDOUT;
+       int i;
+       u8 val = 0;
+
+       for (i = 0; i < 100; i++) {
+               RTSX_READ_REG(chip, SD_DATA_STATE, &val);
+               if (val & SD_DATA_IDLE) {
+                       retval = STATUS_SUCCESS;
+                       break;
+               }
+               udelay(100);
+       }
+       RTSX_DEBUGP("SD_DATA_STATE: 0x%02x\n", val);
+
+       return retval;
+}
+
+static int sd_sdr_tuning_rx_cmd(struct rtsx_chip *chip, u8 sample_point)
+{
+       int retval;
+       u8 cmd[5];
+
+       retval = sd_change_phase(chip, sample_point, TUNE_RX);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       cmd[0] = 0x40 | SEND_TUNING_PATTERN;
+       cmd[1] = 0;
+       cmd[2] = 0;
+       cmd[3] = 0;
+       cmd[4] = 0;
+
+       retval = sd_read_data(chip, SD_TM_AUTO_TUNING,
+                       cmd, 5, 0x40, 1, SD_BUS_WIDTH_4, NULL, 0, 100);
+       if (retval != STATUS_SUCCESS) {
+               (void)sd_wait_data_idle(chip);
+
+               rtsx_clear_sd_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_ddr_tuning_rx_cmd(struct rtsx_chip *chip, u8 sample_point)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       u8 cmd[5];
+
+       retval = sd_change_phase(chip, sample_point, TUNE_RX);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_DEBUGP("sd ddr tuning rx\n");
+
+       retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
+                               SD_RSP_TYPE_R1, NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       cmd[0] = 0x40 | SD_STATUS;
+       cmd[1] = 0;
+       cmd[2] = 0;
+       cmd[3] = 0;
+       cmd[4] = 0;
+
+       retval = sd_read_data(chip, SD_TM_NORMAL_READ,
+                       cmd, 5, 64, 1, SD_BUS_WIDTH_4, NULL, 0, 100);
+       if (retval != STATUS_SUCCESS) {
+               (void)sd_wait_data_idle(chip);
+
+               rtsx_clear_sd_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int mmc_ddr_tunning_rx_cmd(struct rtsx_chip *chip, u8 sample_point)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       u8 cmd[5], bus_width;
+
+       if (CHK_MMC_8BIT(sd_card))
+               bus_width = SD_BUS_WIDTH_8;
+       else if (CHK_MMC_4BIT(sd_card))
+               bus_width = SD_BUS_WIDTH_4;
+       else
+               bus_width = SD_BUS_WIDTH_1;
+
+       retval = sd_change_phase(chip, sample_point, TUNE_RX);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_DEBUGP("mmc ddr tuning rx\n");
+
+       cmd[0] = 0x40 | SEND_EXT_CSD;
+       cmd[1] = 0;
+       cmd[2] = 0;
+       cmd[3] = 0;
+       cmd[4] = 0;
+
+       retval = sd_read_data(chip, SD_TM_NORMAL_READ,
+                       cmd, 5, 0x200, 1, bus_width, NULL, 0, 100);
+       if (retval != STATUS_SUCCESS) {
+               (void)sd_wait_data_idle(chip);
+
+               rtsx_clear_sd_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_sdr_tuning_tx_cmd(struct rtsx_chip *chip, u8 sample_point)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+
+       retval = sd_change_phase(chip, sample_point, TUNE_TX);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_WRITE_REG(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN,
+               SD_RSP_80CLK_TIMEOUT_EN);
+
+       retval = sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+               SD_RSP_TYPE_R1, NULL, 0);
+       if (retval != STATUS_SUCCESS) {
+               if (sd_check_err_code(chip, SD_RSP_TIMEOUT)) {
+                       rtsx_write_register(chip, SD_CFG3,
+                                       SD_RSP_80CLK_TIMEOUT_EN, 0);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       RTSX_WRITE_REG(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, 0);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_ddr_tuning_tx_cmd(struct rtsx_chip *chip, u8 sample_point)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       u8 cmd[5], bus_width;
+
+       retval = sd_change_phase(chip, sample_point, TUNE_TX);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (CHK_SD(sd_card)) {
+               bus_width = SD_BUS_WIDTH_4;
+       } else {
+               if (CHK_MMC_8BIT(sd_card))
+                       bus_width = SD_BUS_WIDTH_8;
+               else if (CHK_MMC_4BIT(sd_card))
+                       bus_width = SD_BUS_WIDTH_4;
+               else
+                       bus_width = SD_BUS_WIDTH_1;
+       }
+
+       retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_WRITE_REG(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN,
+               SD_RSP_80CLK_TIMEOUT_EN);
+
+       cmd[0] = 0x40 | PROGRAM_CSD;
+       cmd[1] = 0;
+       cmd[2] = 0;
+       cmd[3] = 0;
+       cmd[4] = 0;
+
+       retval = sd_write_data(chip, SD_TM_AUTO_WRITE_2,
+                       cmd, 5, 16, 1, bus_width, sd_card->raw_csd, 16, 100);
+       if (retval != STATUS_SUCCESS) {
+               rtsx_clear_sd_error(chip);
+               rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, 0);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       RTSX_WRITE_REG(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, 0);
+
+       sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, SD_RSP_TYPE_R1,
+                       NULL, 0);
+
+       return STATUS_SUCCESS;
+}
+
+static u8 sd_search_final_phase(struct rtsx_chip *chip, u32 phase_map,
+                               u8 tune_dir)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       struct timing_phase_path path[MAX_PHASE + 1];
+       int i, j, cont_path_cnt;
+       int new_block, max_len, final_path_idx;
+       u8 final_phase = 0xFF;
+
+       if (phase_map == 0xFFFFFFFF) {
+               if (tune_dir == TUNE_RX)
+                       final_phase = (u8)chip->sd_default_rx_phase;
+               else
+                       final_phase = (u8)chip->sd_default_tx_phase;
+
+               goto Search_Finish;
+       }
+
+       cont_path_cnt = 0;
+       new_block = 1;
+       j = 0;
+       for (i = 0; i < MAX_PHASE + 1; i++) {
+               if (phase_map & (1 << i)) {
+                       if (new_block) {
+                               new_block = 0;
+                               j = cont_path_cnt++;
+                               path[j].start = i;
+                               path[j].end = i;
+                       } else {
+                               path[j].end = i;
+                       }
+               } else {
+                       new_block = 1;
+                       if (cont_path_cnt) {
+                               int idx = cont_path_cnt - 1;
+                               path[idx].len = path[idx].end -
+                                       path[idx].start + 1;
+                               path[idx].mid = path[idx].start +
+                                       path[idx].len / 2;
+                       }
+               }
+       }
+
+       if (cont_path_cnt == 0) {
+               RTSX_DEBUGP("No continuous phase path\n");
+               goto Search_Finish;
+       } else {
+               int idx = cont_path_cnt - 1;
+               path[idx].len = path[idx].end - path[idx].start + 1;
+               path[idx].mid = path[idx].start + path[idx].len / 2;
+       }
+
+       if ((path[0].start == 0) &&
+               (path[cont_path_cnt - 1].end == MAX_PHASE)) {
+               path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1;
+               path[0].len += path[cont_path_cnt - 1].len;
+               path[0].mid = path[0].start + path[0].len / 2;
+               if (path[0].mid < 0)
+                       path[0].mid += MAX_PHASE + 1;
+
+               cont_path_cnt--;
+       }
+
+       max_len = 0;
+       final_phase = 0;
+       final_path_idx = 0;
+       for (i = 0; i < cont_path_cnt; i++) {
+               if (path[i].len > max_len) {
+                       max_len = path[i].len;
+                       final_phase = (u8)path[i].mid;
+                       final_path_idx = i;
+               }
+
+               RTSX_DEBUGP("path[%d].start = %d\n", i, path[i].start);
+               RTSX_DEBUGP("path[%d].end = %d\n", i, path[i].end);
+               RTSX_DEBUGP("path[%d].len = %d\n", i, path[i].len);
+               RTSX_DEBUGP("path[%d].mid = %d\n", i, path[i].mid);
+               RTSX_DEBUGP("\n");
+       }
+
+       if (tune_dir == TUNE_TX) {
+               if (CHK_SD_SDR104(sd_card)) {
+                       if (max_len > 15) {
+                               int temp_mid = (max_len - 16) / 2;
+                               int temp_final_phase =
+                                       path[final_path_idx].end -
+                                       (max_len - (6 + temp_mid));
+
+                               if (temp_final_phase < 0)
+                                       final_phase = (u8)(temp_final_phase +
+                                                       MAX_PHASE + 1);
+                               else
+                                       final_phase = (u8)temp_final_phase;
+                       }
+               } else if (CHK_SD_SDR50(sd_card)) {
+                       if (max_len > 12) {
+                               int temp_mid = (max_len - 13) / 2;
+                               int temp_final_phase =
+                                       path[final_path_idx].end -
+                                       (max_len - (3 + temp_mid));
+
+                               if (temp_final_phase < 0)
+                                       final_phase = (u8)(temp_final_phase +
+                                                       MAX_PHASE + 1);
+                               else
+                                       final_phase = (u8)temp_final_phase;
+                       }
+               }
+       }
+
+Search_Finish:
+       RTSX_DEBUGP("Final chosen phase: %d\n", final_phase);
+       return final_phase;
+}
+
+static int sd_tuning_rx(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       int i, j;
+       u32 raw_phase_map[3], phase_map;
+       u8 final_phase;
+       int (*tuning_cmd)(struct rtsx_chip *chip, u8 sample_point);
+
+       if (CHK_SD(sd_card)) {
+               if (CHK_SD_DDR50(sd_card))
+                       tuning_cmd = sd_ddr_tuning_rx_cmd;
+               else
+                       tuning_cmd = sd_sdr_tuning_rx_cmd;
+
+       } else {
+               if (CHK_MMC_DDR52(sd_card))
+                       tuning_cmd = mmc_ddr_tunning_rx_cmd;
+               else
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       for (i = 0; i < 3; i++) {
+               raw_phase_map[i] = 0;
+               for (j = MAX_PHASE; j >= 0; j--) {
+                       if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+                               sd_set_err_code(chip, SD_NO_CARD);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       retval = tuning_cmd(chip, (u8)j);
+                       if (retval == STATUS_SUCCESS)
+                               raw_phase_map[i] |= 1 << j;
+               }
+       }
+
+       phase_map = raw_phase_map[0] & raw_phase_map[1] & raw_phase_map[2];
+       for (i = 0; i < 3; i++)
+               RTSX_DEBUGP("RX raw_phase_map[%d] = 0x%08x\n", i,
+                       raw_phase_map[i]);
+
+       RTSX_DEBUGP("RX phase_map = 0x%08x\n", phase_map);
+
+       final_phase = sd_search_final_phase(chip, phase_map, TUNE_RX);
+       if (final_phase == 0xFF)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = sd_change_phase(chip, final_phase, TUNE_RX);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_ddr_pre_tuning_tx(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       int i;
+       u32 phase_map;
+       u8 final_phase;
+
+       RTSX_WRITE_REG(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN,
+               SD_RSP_80CLK_TIMEOUT_EN);
+
+       phase_map = 0;
+       for (i = MAX_PHASE; i >= 0; i--) {
+               if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+                       sd_set_err_code(chip, SD_NO_CARD);
+                       rtsx_write_register(chip, SD_CFG3,
+                                               SD_RSP_80CLK_TIMEOUT_EN, 0);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               retval = sd_change_phase(chip, (u8)i, TUNE_TX);
+               if (retval != STATUS_SUCCESS)
+                       continue;
+
+               retval = sd_send_cmd_get_rsp(chip, SEND_STATUS,
+                                       sd_card->sd_addr, SD_RSP_TYPE_R1, NULL,
+                                       0);
+               if ((retval == STATUS_SUCCESS) ||
+                       !sd_check_err_code(chip, SD_RSP_TIMEOUT))
+                       phase_map |= 1 << i;
+       }
+
+       RTSX_WRITE_REG(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, 0);
+
+       RTSX_DEBUGP("DDR TX pre tune phase_map = 0x%08x\n", phase_map);
+
+       final_phase = sd_search_final_phase(chip, phase_map, TUNE_TX);
+       if (final_phase == 0xFF)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = sd_change_phase(chip, final_phase, TUNE_TX);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_DEBUGP("DDR TX pre tune phase: %d\n", (int)final_phase);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_tuning_tx(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       int i, j;
+       u32 raw_phase_map[3], phase_map;
+       u8 final_phase;
+       int (*tuning_cmd)(struct rtsx_chip *chip, u8 sample_point);
+
+       if (CHK_SD(sd_card)) {
+               if (CHK_SD_DDR50(sd_card))
+                       tuning_cmd = sd_ddr_tuning_tx_cmd;
+               else
+                       tuning_cmd = sd_sdr_tuning_tx_cmd;
+
+       } else {
+               if (CHK_MMC_DDR52(sd_card))
+                       tuning_cmd = sd_ddr_tuning_tx_cmd;
+               else
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       for (i = 0; i < 3; i++) {
+               raw_phase_map[i] = 0;
+               for (j = MAX_PHASE; j >= 0; j--) {
+                       if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+                               sd_set_err_code(chip, SD_NO_CARD);
+                               rtsx_write_register(chip, SD_CFG3,
+                                                   SD_RSP_80CLK_TIMEOUT_EN, 0);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       retval = tuning_cmd(chip, (u8)j);
+                       if (retval == STATUS_SUCCESS)
+                               raw_phase_map[i] |= 1 << j;
+               }
+       }
+
+       phase_map = raw_phase_map[0] & raw_phase_map[1] & raw_phase_map[2];
+       for (i = 0; i < 3; i++)
+               RTSX_DEBUGP("TX raw_phase_map[%d] = 0x%08x\n",
+                       i, raw_phase_map[i]);
+
+       RTSX_DEBUGP("TX phase_map = 0x%08x\n", phase_map);
+
+       final_phase = sd_search_final_phase(chip, phase_map, TUNE_TX);
+       if (final_phase == 0xFF)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = sd_change_phase(chip, final_phase, TUNE_TX);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_sdr_tuning(struct rtsx_chip *chip)
+{
+       int retval;
+
+       retval = sd_tuning_tx(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = sd_tuning_rx(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_ddr_tuning(struct rtsx_chip *chip)
+{
+       int retval;
+
+       if (!(chip->sd_ctl & SD_DDR_TX_PHASE_SET_BY_USER)) {
+               retval = sd_ddr_pre_tuning_tx(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       } else {
+               retval = sd_change_phase(chip, (u8)chip->sd_ddr_tx_phase,
+                                       TUNE_TX);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = sd_tuning_rx(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (!(chip->sd_ctl & SD_DDR_TX_PHASE_SET_BY_USER)) {
+               retval = sd_tuning_tx(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int mmc_ddr_tuning(struct rtsx_chip *chip)
+{
+       int retval;
+
+       if (!(chip->sd_ctl & MMC_DDR_TX_PHASE_SET_BY_USER)) {
+               retval = sd_ddr_pre_tuning_tx(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       } else {
+               retval = sd_change_phase(chip, (u8)chip->mmc_ddr_tx_phase,
+                                       TUNE_TX);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = sd_tuning_rx(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (!(chip->sd_ctl & MMC_DDR_TX_PHASE_SET_BY_USER)) {
+               retval = sd_tuning_tx(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int sd_switch_clock(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       int re_tuning = 0;
+
+       retval = select_card(chip, SD_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = switch_clock(chip, sd_card->sd_clock);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (re_tuning) {
+               if (CHK_SD(sd_card)) {
+                       if (CHK_SD_DDR50(sd_card))
+                               retval = sd_ddr_tuning(chip);
+                       else
+                               retval = sd_sdr_tuning(chip);
+               } else {
+                       if (CHK_MMC_DDR52(sd_card))
+                               retval = mmc_ddr_tuning(chip);
+               }
+
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_prepare_reset(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+
+       if (chip->asic_code)
+               sd_card->sd_clock = 29;
+       else
+               sd_card->sd_clock = CLK_30;
+
+       sd_card->sd_type = 0;
+       sd_card->seq_mode = 0;
+       sd_card->sd_data_buf_ready = 0;
+       sd_card->capacity = 0;
+
+#ifdef SUPPORT_SD_LOCK
+       sd_card->sd_lock_status = 0;
+       sd_card->sd_erase_status = 0;
+#endif
+
+       chip->capacity[chip->card2lun[SD_CARD]] = 0;
+       chip->sd_io = 0;
+
+       retval = sd_set_init_para(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, retval);
+
+       RTSX_WRITE_REG(chip, REG_SD_CFG1, 0xFF, 0x40);
+
+       RTSX_WRITE_REG(chip, CARD_STOP, SD_STOP | SD_CLR_ERR,
+               SD_STOP | SD_CLR_ERR);
+
+       retval = select_card(chip, SD_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_pull_ctl_disable(struct rtsx_chip *chip)
+{
+       if (CHECK_PID(chip, 0x5208)) {
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL1, 0xFF,
+                       XD_D3_PD | SD_D7_PD | SD_CLK_PD | SD_D5_PD);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL2, 0xFF,
+                       SD_D6_PD | SD_D0_PD | SD_D1_PD | XD_D5_PD);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL3, 0xFF,
+                       SD_D4_PD | XD_CE_PD | XD_CLE_PD | XD_CD_PU);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL4, 0xFF,
+                       XD_RDY_PD | SD_D3_PD | SD_D2_PD | XD_ALE_PD);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL5, 0xFF,
+                       MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL6, 0xFF, MS_D5_PD | MS_D4_PD);
+       } else if (CHECK_PID(chip, 0x5288)) {
+               if (CHECK_BARO_PKG(chip, QFN)) {
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL1, 0xFF, 0x55);
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL2, 0xFF, 0x55);
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL3, 0xFF, 0x4B);
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL4, 0xFF, 0x69);
+               }
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int sd_pull_ctl_enable(struct rtsx_chip *chip)
+{
+       int retval;
+
+       rtsx_init_cmd(chip);
+
+       if (CHECK_PID(chip, 0x5208)) {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF,
+                       XD_D3_PD | SD_DAT7_PU | SD_CLK_NP | SD_D5_PU);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF,
+                       SD_D6_PU | SD_D0_PU | SD_D1_PU | XD_D5_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF,
+                       SD_D4_PU | XD_CE_PD | XD_CLE_PD | XD_CD_PU);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF,
+                       XD_RDY_PD | SD_D3_PU | SD_D2_PU | XD_ALE_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF,
+                       MS_INS_PU | SD_WP_PU | SD_CD_PU | SD_CMD_PU);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF,
+                       MS_D5_PD | MS_D4_PD);
+       } else if (CHECK_PID(chip, 0x5288)) {
+               if (CHECK_BARO_PKG(chip, QFN)) {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF,
+                               0xA8);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF,
+                               0x5A);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF,
+                               0x95);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF,
+                               0xAA);
+               }
+       }
+
+       retval = rtsx_send_cmd(chip, SD_CARD, 100);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_init_power(struct rtsx_chip *chip)
+{
+       int retval;
+
+       retval = sd_power_off_card3v3(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (!chip->ft2_fast_mode)
+               wait_timeout(250);
+
+       retval = enable_card_clock(chip, SD_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (chip->asic_code) {
+               retval = sd_pull_ctl_enable(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       } else {
+               RTSX_WRITE_REG(chip, FPGA_PULL_CTL, FPGA_SD_PULL_CTL_BIT | 0x20,
+                       0);
+       }
+
+       if (!chip->ft2_fast_mode) {
+               retval = card_power_on(chip, SD_CARD);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               wait_timeout(260);
+
+#ifdef SUPPORT_OCP
+               if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
+                       RTSX_DEBUGP("Over current, OCPSTAT is 0x%x\n",
+                               chip->ocp_stat);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+#endif
+       }
+
+       RTSX_WRITE_REG(chip, CARD_OE, SD_OUTPUT_EN, SD_OUTPUT_EN);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_dummy_clock(struct rtsx_chip *chip)
+{
+       RTSX_WRITE_REG(chip, REG_SD_CFG3, 0x01, 0x01);
+       wait_timeout(5);
+       RTSX_WRITE_REG(chip, REG_SD_CFG3, 0x01, 0);
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_read_lba0(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       u8 cmd[5], bus_width;
+
+       cmd[0] = 0x40 | READ_SINGLE_BLOCK;
+       cmd[1] = 0;
+       cmd[2] = 0;
+       cmd[3] = 0;
+       cmd[4] = 0;
+
+       if (CHK_SD(sd_card)) {
+               bus_width = SD_BUS_WIDTH_4;
+       } else {
+               if (CHK_MMC_8BIT(sd_card))
+                       bus_width = SD_BUS_WIDTH_8;
+               else if (CHK_MMC_4BIT(sd_card))
+                       bus_width = SD_BUS_WIDTH_4;
+               else
+                       bus_width = SD_BUS_WIDTH_1;
+       }
+
+       retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd,
+               5, 512, 1, bus_width, NULL, 0, 100);
+       if (retval != STATUS_SUCCESS) {
+               rtsx_clear_sd_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sd_check_wp_state(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       u32 val;
+       u16 sd_card_type;
+       u8 cmd[5], buf[64];
+
+       retval = sd_send_cmd_get_rsp(chip, APP_CMD,
+                       sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       cmd[0] = 0x40 | SD_STATUS;
+       cmd[1] = 0;
+       cmd[2] = 0;
+       cmd[3] = 0;
+       cmd[4] = 0;
+
+       retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, 64, 1,
+                       SD_BUS_WIDTH_4, buf, 64, 250);
+       if (retval != STATUS_SUCCESS) {
+               rtsx_clear_sd_error(chip);
+
+               sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+                               SD_RSP_TYPE_R1, NULL, 0);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       RTSX_DEBUGP("ACMD13:\n");
+       RTSX_DUMP(buf, 64);
+
+       sd_card_type = ((u16)buf[2] << 8) | buf[3];
+       RTSX_DEBUGP("sd_card_type = 0x%04x\n", sd_card_type);
+       if ((sd_card_type == 0x0001) || (sd_card_type == 0x0002)) {
+               /* ROM card or OTP */
+               chip->card_wp |= SD_CARD;
+       }
+
+       /* Check SD Machanical Write-Protect Switch */
+       val = rtsx_readl(chip, RTSX_BIPR);
+       if (val & SD_WRITE_PROTECT)
+               chip->card_wp |= SD_CARD;
+
+       return STATUS_SUCCESS;
+}
+
+static int reset_sd(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval, i = 0, j = 0, k = 0, hi_cap_flow = 0;
+       int sd_dont_switch = 0;
+       int support_1v8 = 0;
+       int try_sdio = 1;
+       u8 rsp[16];
+       u8 switch_bus_width;
+       u32 voltage = 0;
+       int sd20_mode = 0;
+
+       SET_SD(sd_card);
+
+Switch_Fail:
+
+       i = 0;
+       j = 0;
+       k = 0;
+       hi_cap_flow = 0;
+
+#ifdef SUPPORT_SD_LOCK
+       if (sd_card->sd_lock_status & SD_UNLOCK_POW_ON)
+               goto SD_UNLOCK_ENTRY;
+#endif
+
+       retval = sd_prepare_reset(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = sd_dummy_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip) && try_sdio) {
+               int rty_cnt = 0;
+
+               for (; rty_cnt < chip->sdio_retry_cnt; rty_cnt++) {
+                       if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+                               sd_set_err_code(chip, SD_NO_CARD);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       retval = sd_send_cmd_get_rsp(chip, IO_SEND_OP_COND, 0,
+                                               SD_RSP_TYPE_R4, rsp, 5);
+                       if (retval == STATUS_SUCCESS) {
+                               int func_num = (rsp[1] >> 4) & 0x07;
+                               if (func_num) {
+                                       RTSX_DEBUGP("SD_IO card (Function number: %d)!\n", func_num);
+                                       chip->sd_io = 1;
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               }
+
+                               break;
+                       }
+
+                       sd_init_power(chip);
+
+                       sd_dummy_clock(chip);
+               }
+
+               RTSX_DEBUGP("Normal card!\n");
+       }
+
+       /* Start Initialization Process of SD Card */
+RTY_SD_RST:
+       retval = sd_send_cmd_get_rsp(chip, GO_IDLE_STATE, 0, SD_RSP_TYPE_R0,
+                               NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       wait_timeout(20);
+
+       retval = sd_send_cmd_get_rsp(chip, SEND_IF_COND, 0x000001AA,
+                               SD_RSP_TYPE_R7, rsp, 5);
+       if (retval == STATUS_SUCCESS) {
+               if ((rsp[4] == 0xAA) && ((rsp[3] & 0x0f) == 0x01)) {
+                       hi_cap_flow = 1;
+                       voltage = SUPPORT_VOLTAGE | 0x40000000;
+               }
+       }
+
+       if (!hi_cap_flow) {
+               voltage = SUPPORT_VOLTAGE;
+
+               retval = sd_send_cmd_get_rsp(chip, GO_IDLE_STATE, 0,
+                                       SD_RSP_TYPE_R0, NULL, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               wait_timeout(20);
+       }
+
+       do {
+               retval = sd_send_cmd_get_rsp(chip, APP_CMD, 0, SD_RSP_TYPE_R1,
+                                       NULL, 0);
+               if (retval != STATUS_SUCCESS) {
+                       if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+                               sd_set_err_code(chip, SD_NO_CARD);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       j++;
+                       if (j < 3)
+                               goto RTY_SD_RST;
+                       else
+                               TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               retval = sd_send_cmd_get_rsp(chip, SD_APP_OP_COND, voltage,
+                                       SD_RSP_TYPE_R3, rsp, 5);
+               if (retval != STATUS_SUCCESS) {
+                       k++;
+                       if (k < 3)
+                               goto RTY_SD_RST;
+                       else
+                               TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               i++;
+               wait_timeout(20);
+       } while (!(rsp[1] & 0x80) && (i < 255));
+
+       if (i == 255)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (hi_cap_flow) {
+               if (rsp[1] & 0x40)
+                       SET_SD_HCXC(sd_card);
+               else
+                       CLR_SD_HCXC(sd_card);
+
+               support_1v8 = 0;
+       } else {
+               CLR_SD_HCXC(sd_card);
+               support_1v8 = 0;
+       }
+       RTSX_DEBUGP("support_1v8 = %d\n", support_1v8);
+
+       if (support_1v8) {
+               retval = sd_voltage_switch(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = sd_send_cmd_get_rsp(chip, ALL_SEND_CID, 0, SD_RSP_TYPE_R2,
+                               NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       for (i = 0; i < 3; i++) {
+               retval = sd_send_cmd_get_rsp(chip, SEND_RELATIVE_ADDR, 0,
+                                       SD_RSP_TYPE_R6, rsp, 5);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               sd_card->sd_addr = (u32)rsp[1] << 24;
+               sd_card->sd_addr += (u32)rsp[2] << 16;
+
+               if (sd_card->sd_addr)
+                       break;
+       }
+
+       retval = sd_check_csd(chip, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = sd_select_card(chip, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+#ifdef SUPPORT_SD_LOCK
+SD_UNLOCK_ENTRY:
+       retval = sd_update_lock_status(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (sd_card->sd_lock_status & SD_LOCKED) {
+               sd_card->sd_lock_status |= (SD_LOCK_1BIT_MODE | SD_PWD_EXIST);
+               return STATUS_SUCCESS;
+       } else if (!(sd_card->sd_lock_status & SD_UNLOCK_POW_ON)) {
+               sd_card->sd_lock_status &= ~SD_PWD_EXIST;
+       }
+#endif
+
+       retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
+                               SD_RSP_TYPE_R1, NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = sd_send_cmd_get_rsp(chip, SET_CLR_CARD_DETECT, 0,
+                               SD_RSP_TYPE_R1, NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (support_1v8) {
+               retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
+                                       SD_RSP_TYPE_R1, NULL, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = sd_send_cmd_get_rsp(chip, SET_BUS_WIDTH, 2,
+                                       SD_RSP_TYPE_R1, NULL, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               switch_bus_width = SD_BUS_WIDTH_4;
+       } else {
+               switch_bus_width = SD_BUS_WIDTH_1;
+       }
+
+       retval = sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200, SD_RSP_TYPE_R1,
+                               NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (!(sd_card->raw_csd[4] & 0x40))
+               sd_dont_switch = 1;
+
+       if (!sd_dont_switch) {
+               if (sd20_mode) {
+                       /* Set sd_switch_fail here, because we needn't
+                        * switch to UHS mode
+                        */
+                       sd_card->sd_switch_fail = SDR104_SUPPORT_MASK |
+                               DDR50_SUPPORT_MASK | SDR50_SUPPORT_MASK;
+               }
+
+               /* Check the card whether follow SD1.1 spec or higher */
+               retval = sd_check_spec(chip, switch_bus_width);
+               if (retval == STATUS_SUCCESS) {
+                       retval = sd_switch_function(chip, switch_bus_width);
+                       if (retval != STATUS_SUCCESS) {
+                               sd_init_power(chip);
+                               sd_dont_switch = 1;
+                               try_sdio = 0;
+
+                               goto Switch_Fail;
+                       }
+               } else {
+                       if (support_1v8) {
+                               sd_init_power(chip);
+                               sd_dont_switch = 1;
+                               try_sdio = 0;
+
+                               goto Switch_Fail;
+                       }
+               }
+       }
+
+       if (!support_1v8) {
+               retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
+                                       SD_RSP_TYPE_R1, NULL, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = sd_send_cmd_get_rsp(chip, SET_BUS_WIDTH, 2,
+                                       SD_RSP_TYPE_R1, NULL, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+#ifdef SUPPORT_SD_LOCK
+       sd_card->sd_lock_status &= ~SD_LOCK_1BIT_MODE;
+#endif
+
+       if (!sd20_mode && CHK_SD30_SPEED(sd_card)) {
+               int read_lba0 = 1;
+
+               RTSX_WRITE_REG(chip, SD30_DRIVE_SEL, 0x07,
+                       chip->sd30_drive_sel_1v8);
+
+               retval = sd_set_init_para(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if (CHK_SD_DDR50(sd_card))
+                       retval = sd_ddr_tuning(chip);
+               else
+                       retval = sd_sdr_tuning(chip);
+
+               if (retval != STATUS_SUCCESS) {
+                       if (sd20_mode) {
+                               TRACE_RET(chip, STATUS_FAIL);
+                       } else {
+                               retval = sd_init_power(chip);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+
+                               try_sdio = 0;
+                               sd20_mode = 1;
+                               goto Switch_Fail;
+                       }
+               }
+
+               sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+                               SD_RSP_TYPE_R1, NULL, 0);
+
+               if (CHK_SD_DDR50(sd_card)) {
+                       retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000);
+                       if (retval != STATUS_SUCCESS)
+                               read_lba0 = 0;
+               }
+
+               if (read_lba0) {
+                       retval = sd_read_lba0(chip);
+                       if (retval != STATUS_SUCCESS) {
+                               if (sd20_mode) {
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               } else {
+                                       retval = sd_init_power(chip);
+                                       if (retval != STATUS_SUCCESS)
+                                               TRACE_RET(chip, STATUS_FAIL);
+
+                                       try_sdio = 0;
+                                       sd20_mode = 1;
+                                       goto Switch_Fail;
+                               }
+                       }
+               }
+       }
+
+       retval = sd_check_wp_state(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       chip->card_bus_width[chip->card2lun[SD_CARD]] = 4;
+
+#ifdef SUPPORT_SD_LOCK
+       if (sd_card->sd_lock_status & SD_UNLOCK_POW_ON) {
+               RTSX_WRITE_REG(chip, REG_SD_BLOCK_CNT_H, 0xFF, 0x02);
+               RTSX_WRITE_REG(chip, REG_SD_BLOCK_CNT_L, 0xFF, 0x00);
+       }
+#endif
+
+       return STATUS_SUCCESS;
+}
+
+
+static int mmc_test_switch_bus(struct rtsx_chip *chip, u8 width)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       u8 buf[8] = {0}, bus_width, *ptr;
+       u16 byte_cnt;
+       int len;
+
+       retval = sd_send_cmd_get_rsp(chip, BUSTEST_W, 0, SD_RSP_TYPE_R1, NULL,
+                               0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, SWITCH_FAIL);
+
+       if (width == MMC_8BIT_BUS) {
+               buf[0] = 0x55;
+               buf[1] = 0xAA;
+               len = 8;
+               byte_cnt = 8;
+               bus_width = SD_BUS_WIDTH_8;
+       } else {
+               buf[0] = 0x5A;
+               len = 4;
+               byte_cnt = 4;
+               bus_width = SD_BUS_WIDTH_4;
+       }
+
+       retval = rtsx_write_register(chip, REG_SD_CFG3, 0x02, 0x02);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, SWITCH_ERR);
+
+       retval = sd_write_data(chip, SD_TM_AUTO_WRITE_3,
+                       NULL, 0, byte_cnt, 1, bus_width, buf, len, 100);
+       if (retval != STATUS_SUCCESS) {
+               rtsx_clear_sd_error(chip);
+               rtsx_write_register(chip, REG_SD_CFG3, 0x02, 0);
+               TRACE_RET(chip, SWITCH_ERR);
+       }
+
+       retval = rtsx_write_register(chip, REG_SD_CFG3, 0x02, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, SWITCH_ERR);
+
+       RTSX_DEBUGP("SD/MMC CMD %d\n", BUSTEST_R);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, 0x40 | BUSTEST_R);
+
+       if (width == MMC_8BIT_BUS)
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L,
+                       0xFF, 0x08);
+       else
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L,
+                       0xFF, 0x04);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF, 1);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF, 0);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
+               SD_CALCULATE_CRC7 | SD_NO_CHECK_CRC16 | SD_NO_WAIT_BUSY_END|
+               SD_CHECK_CRC7 | SD_RSP_LEN_6);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01,
+               PINGPONG_BUFFER);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+               SD_TM_NORMAL_READ | SD_TRANSFER_START);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END,
+               SD_TRANSFER_END);
+
+       rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2, 0, 0);
+       if (width == MMC_8BIT_BUS)
+               rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 1, 0, 0);
+
+       retval = rtsx_send_cmd(chip, SD_CARD, 100);
+       if (retval < 0) {
+               rtsx_clear_sd_error(chip);
+               TRACE_RET(chip, SWITCH_ERR);
+       }
+
+       ptr = rtsx_get_cmd_data(chip) + 1;
+
+       if (width == MMC_8BIT_BUS) {
+               RTSX_DEBUGP("BUSTEST_R [8bits]: 0x%02x 0x%02x\n", ptr[0],
+                       ptr[1]);
+               if ((ptr[0] == 0xAA) && (ptr[1] == 0x55)) {
+                       u8 rsp[5];
+                       u32 arg;
+
+                       if (CHK_MMC_DDR52(sd_card))
+                               arg = 0x03B70600;
+                       else
+                               arg = 0x03B70200;
+
+                       retval = sd_send_cmd_get_rsp(chip, SWITCH, arg,
+                                               SD_RSP_TYPE_R1b, rsp, 5);
+                       if ((retval == STATUS_SUCCESS) &&
+                               !(rsp[4] & MMC_SWITCH_ERR))
+                               return SWITCH_SUCCESS;
+               }
+       } else {
+               RTSX_DEBUGP("BUSTEST_R [4bits]: 0x%02x\n", ptr[0]);
+               if (ptr[0] == 0xA5) {
+                       u8 rsp[5];
+                       u32 arg;
+
+                       if (CHK_MMC_DDR52(sd_card))
+                               arg = 0x03B70500;
+                       else
+                               arg = 0x03B70100;
+
+                       retval = sd_send_cmd_get_rsp(chip, SWITCH, arg,
+                                               SD_RSP_TYPE_R1b, rsp, 5);
+                       if ((retval == STATUS_SUCCESS) &&
+                               !(rsp[4] & MMC_SWITCH_ERR))
+                               return SWITCH_SUCCESS;
+               }
+       }
+
+       TRACE_RET(chip, SWITCH_FAIL);
+}
+
+
+static int mmc_switch_timing_bus(struct rtsx_chip *chip, int switch_ddr)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+       u8 *ptr, card_type, card_type_mask = 0;
+
+       CLR_MMC_HS(sd_card);
+
+       RTSX_DEBUGP("SD/MMC CMD %d\n", SEND_EXT_CSD);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF,
+               0x40 | SEND_EXT_CSD);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF, 0);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF, 0);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF, 0);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF, 0);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, 0);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF, 2);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF, 1);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF, 0);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
+               SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END|
+               SD_CHECK_CRC7 | SD_RSP_LEN_6);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01,
+               PINGPONG_BUFFER);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+               SD_TM_NORMAL_READ | SD_TRANSFER_START);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END,
+               SD_TRANSFER_END);
+
+       rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 196, 0xFF, 0);
+       rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 212, 0xFF, 0);
+       rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 213, 0xFF, 0);
+       rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 214, 0xFF, 0);
+       rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 215, 0xFF, 0);
+
+       retval = rtsx_send_cmd(chip, SD_CARD, 1000);
+       if (retval < 0) {
+               if (retval == -ETIMEDOUT) {
+                       rtsx_clear_sd_error(chip);
+                       sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+                                       SD_RSP_TYPE_R1, NULL, 0);
+               }
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       ptr = rtsx_get_cmd_data(chip);
+       if (ptr[0] & SD_TRANSFER_ERR) {
+               sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+                               SD_RSP_TYPE_R1, NULL, 0);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (CHK_MMC_SECTOR_MODE(sd_card)) {
+               sd_card->capacity = ((u32)ptr[5] << 24) | ((u32)ptr[4] << 16) |
+                       ((u32)ptr[3] << 8) | ((u32)ptr[2]);
+       }
+
+       card_type_mask = 0x03;
+       card_type = ptr[1] & card_type_mask;
+       if (card_type) {
+               u8 rsp[5];
+
+               if (card_type & 0x04) {
+                       if (switch_ddr)
+                               SET_MMC_DDR52(sd_card);
+                       else
+                               SET_MMC_52M(sd_card);
+               } else if (card_type & 0x02) {
+                       SET_MMC_52M(sd_card);
+               } else {
+                       SET_MMC_26M(sd_card);
+               }
+
+               retval = sd_send_cmd_get_rsp(chip, SWITCH,
+                               0x03B90100, SD_RSP_TYPE_R1b, rsp, 5);
+               if ((retval != STATUS_SUCCESS) || (rsp[4] & MMC_SWITCH_ERR))
+                       CLR_MMC_HS(sd_card);
+       }
+
+       sd_choose_proper_clock(chip);
+       retval = switch_clock(chip, sd_card->sd_clock);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       /* Test Bus Procedure */
+       retval = mmc_test_switch_bus(chip, MMC_8BIT_BUS);
+       if (retval == SWITCH_SUCCESS) {
+               SET_MMC_8BIT(sd_card);
+               chip->card_bus_width[chip->card2lun[SD_CARD]] = 8;
+#ifdef SUPPORT_SD_LOCK
+               sd_card->sd_lock_status &= ~SD_LOCK_1BIT_MODE;
+#endif
+       } else if (retval == SWITCH_FAIL) {
+               retval = mmc_test_switch_bus(chip, MMC_4BIT_BUS);
+               if (retval == SWITCH_SUCCESS) {
+                       SET_MMC_4BIT(sd_card);
+                       chip->card_bus_width[chip->card2lun[SD_CARD]] = 4;
+#ifdef SUPPORT_SD_LOCK
+                       sd_card->sd_lock_status &= ~SD_LOCK_1BIT_MODE;
+#endif
+               } else if (retval == SWITCH_FAIL) {
+                       CLR_MMC_8BIT(sd_card);
+                       CLR_MMC_4BIT(sd_card);
+               } else {
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       } else {
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+
+static int reset_mmc(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval, i = 0, j = 0, k = 0;
+       int switch_ddr = 1;
+       u8 rsp[16];
+       u8 spec_ver = 0;
+       u32 temp;
+
+#ifdef SUPPORT_SD_LOCK
+       if (sd_card->sd_lock_status & SD_UNLOCK_POW_ON)
+               goto MMC_UNLOCK_ENTRY;
+#endif
+
+Switch_Fail:
+       retval = sd_prepare_reset(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, retval);
+
+       SET_MMC(sd_card);
+
+RTY_MMC_RST:
+       retval = sd_send_cmd_get_rsp(chip, GO_IDLE_STATE, 0, SD_RSP_TYPE_R0,
+                               NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       do {
+               if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+                       sd_set_err_code(chip, SD_NO_CARD);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               retval = sd_send_cmd_get_rsp(chip, SEND_OP_COND,
+                                       (SUPPORT_VOLTAGE | 0x40000000),
+                                       SD_RSP_TYPE_R3, rsp, 5);
+               if (retval != STATUS_SUCCESS) {
+                       if (sd_check_err_code(chip, SD_BUSY) ||
+                               sd_check_err_code(chip, SD_TO_ERR)) {
+                               k++;
+                               if (k < 20) {
+                                       sd_clr_err_code(chip);
+                                       goto RTY_MMC_RST;
+                               } else {
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               }
+                       } else {
+                               j++;
+                               if (j < 100) {
+                                       sd_clr_err_code(chip);
+                                       goto RTY_MMC_RST;
+                               } else {
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               }
+                       }
+               }
+
+               wait_timeout(20);
+               i++;
+       } while (!(rsp[1] & 0x80) && (i < 255));
+
+       if (i == 255)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if ((rsp[1] & 0x60) == 0x40)
+               SET_MMC_SECTOR_MODE(sd_card);
+       else
+               CLR_MMC_SECTOR_MODE(sd_card);
+
+       retval = sd_send_cmd_get_rsp(chip, ALL_SEND_CID, 0, SD_RSP_TYPE_R2,
+                               NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       sd_card->sd_addr = 0x00100000;
+       retval = sd_send_cmd_get_rsp(chip, SET_RELATIVE_ADDR, sd_card->sd_addr,
+                               SD_RSP_TYPE_R6, rsp, 5);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = sd_check_csd(chip, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       spec_ver = (sd_card->raw_csd[0] & 0x3C) >> 2;
+
+       retval = sd_select_card(chip, 1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200, SD_RSP_TYPE_R1,
+                               NULL, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+#ifdef SUPPORT_SD_LOCK
+MMC_UNLOCK_ENTRY:
+       retval = sd_update_lock_status(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+#endif
+
+       retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       chip->card_bus_width[chip->card2lun[SD_CARD]] = 1;
+
+       if (!sd_card->mmc_dont_switch_bus) {
+               if (spec_ver == 4) {
+                       /* MMC 4.x Cards */
+                       retval = mmc_switch_timing_bus(chip, switch_ddr);
+                       if (retval != STATUS_SUCCESS) {
+                               retval = sd_init_power(chip);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               sd_card->mmc_dont_switch_bus = 1;
+                               TRACE_GOTO(chip, Switch_Fail);
+                       }
+               }
+
+               if (CHK_MMC_SECTOR_MODE(sd_card) && (sd_card->capacity == 0))
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if (switch_ddr && CHK_MMC_DDR52(sd_card)) {
+                       retval = sd_set_init_para(chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       retval = mmc_ddr_tuning(chip);
+                       if (retval != STATUS_SUCCESS) {
+                               retval = sd_init_power(chip);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+
+                               switch_ddr = 0;
+                               TRACE_GOTO(chip, Switch_Fail);
+                       }
+
+                       retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000);
+                       if (retval == STATUS_SUCCESS) {
+                               retval = sd_read_lba0(chip);
+                               if (retval != STATUS_SUCCESS) {
+                                       retval = sd_init_power(chip);
+                                       if (retval != STATUS_SUCCESS)
+                                               TRACE_RET(chip, STATUS_FAIL);
+
+                                       switch_ddr = 0;
+                                       TRACE_GOTO(chip, Switch_Fail);
+                               }
+                       }
+               }
+       }
+
+#ifdef SUPPORT_SD_LOCK
+       if (sd_card->sd_lock_status & SD_UNLOCK_POW_ON) {
+               RTSX_WRITE_REG(chip, REG_SD_BLOCK_CNT_H, 0xFF, 0x02);
+               RTSX_WRITE_REG(chip, REG_SD_BLOCK_CNT_L, 0xFF, 0x00);
+       }
+#endif
+
+       temp = rtsx_readl(chip, RTSX_BIPR);
+       if (temp & SD_WRITE_PROTECT)
+               chip->card_wp |= SD_CARD;
+
+       return STATUS_SUCCESS;
+}
+
+int reset_sd_card(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+
+       sd_init_reg_addr(chip);
+
+       memset(sd_card, 0, sizeof(struct sd_info));
+       chip->capacity[chip->card2lun[SD_CARD]] = 0;
+
+       retval = enable_card_clock(chip, SD_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (chip->ignore_sd && CHK_SDIO_EXIST(chip) &&
+               !CHK_SDIO_IGNORED(chip)) {
+               if (chip->asic_code) {
+                       retval = sd_pull_ctl_enable(chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+               } else {
+                       retval = rtsx_write_register(chip, FPGA_PULL_CTL,
+                                               FPGA_SD_PULL_CTL_BIT | 0x20, 0);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+               }
+               retval = card_share_mode(chip, SD_CARD);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               chip->sd_io = 1;
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = sd_init_power(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (chip->sd_ctl & RESET_MMC_FIRST) {
+               retval = reset_mmc(chip);
+               if (retval != STATUS_SUCCESS) {
+                       if (sd_check_err_code(chip, SD_NO_CARD))
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       retval = reset_sd(chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+               }
+       } else {
+               retval = reset_sd(chip);
+               if (retval != STATUS_SUCCESS) {
+                       if (sd_check_err_code(chip, SD_NO_CARD))
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       if (chip->sd_io) {
+                               TRACE_RET(chip, STATUS_FAIL);
+                       } else {
+                               retval = reset_mmc(chip);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+       }
+
+       retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_WRITE_REG(chip, REG_SD_BYTE_CNT_L, 0xFF, 0);
+       RTSX_WRITE_REG(chip, REG_SD_BYTE_CNT_H, 0xFF, 2);
+
+       chip->capacity[chip->card2lun[SD_CARD]] = sd_card->capacity;
+
+       retval = sd_set_init_para(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_DEBUGP("sd_card->sd_type = 0x%x\n", sd_card->sd_type);
+
+       return STATUS_SUCCESS;
+}
+
+static int reset_mmc_only(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+
+       sd_card->sd_type = 0;
+       sd_card->seq_mode = 0;
+       sd_card->sd_data_buf_ready = 0;
+       sd_card->capacity = 0;
+       sd_card->sd_switch_fail = 0;
+
+#ifdef SUPPORT_SD_LOCK
+       sd_card->sd_lock_status = 0;
+       sd_card->sd_erase_status = 0;
+#endif
+
+       chip->capacity[chip->card2lun[SD_CARD]] = sd_card->capacity = 0;
+
+       retval = enable_card_clock(chip, SD_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = sd_init_power(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = reset_mmc(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_WRITE_REG(chip, REG_SD_BYTE_CNT_L, 0xFF, 0);
+       RTSX_WRITE_REG(chip, REG_SD_BYTE_CNT_H, 0xFF, 2);
+
+       chip->capacity[chip->card2lun[SD_CARD]] = sd_card->capacity;
+
+       retval = sd_set_init_para(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_DEBUGP("In reset_mmc_only, sd_card->sd_type = 0x%x\n",
+               sd_card->sd_type);
+
+       return STATUS_SUCCESS;
+}
+
+#define WAIT_DATA_READY_RTY_CNT                255
+
+static int wait_data_buf_ready(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int i, retval;
+
+       for (i = 0; i < WAIT_DATA_READY_RTY_CNT; i++) {
+               if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+                       sd_set_err_code(chip, SD_NO_CARD);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               sd_card->sd_data_buf_ready = 0;
+
+               retval = sd_send_cmd_get_rsp(chip, SEND_STATUS,
+                               sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if (sd_card->sd_data_buf_ready) {
+                       return sd_send_cmd_get_rsp(chip, SEND_STATUS,
+                               sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
+               }
+       }
+
+       sd_set_err_code(chip, SD_TO_ERR);
+
+       TRACE_RET(chip, STATUS_FAIL);
+}
+
+void sd_stop_seq_mode(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+
+       if (sd_card->seq_mode) {
+               retval = sd_switch_clock(chip);
+               if (retval != STATUS_SUCCESS)
+                       return;
+
+               retval = sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION, 0,
+                               SD_RSP_TYPE_R1b, NULL, 0);
+               if (retval != STATUS_SUCCESS)
+                       sd_set_err_code(chip, SD_STS_ERR);
+
+               retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000);
+               if (retval != STATUS_SUCCESS)
+                       sd_set_err_code(chip, SD_STS_ERR);
+
+               sd_card->seq_mode = 0;
+
+               rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
+       }
+}
+
+static inline int sd_auto_tune_clock(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+
+       if (chip->asic_code) {
+               if (sd_card->sd_clock > 30)
+                       sd_card->sd_clock -= 20;
+       } else {
+               switch (sd_card->sd_clock) {
+               case CLK_200:
+                       sd_card->sd_clock = CLK_150;
+                       break;
+
+               case CLK_150:
+                       sd_card->sd_clock = CLK_120;
+                       break;
+
+               case CLK_120:
+                       sd_card->sd_clock = CLK_100;
+                       break;
+
+               case CLK_100:
+                       sd_card->sd_clock = CLK_80;
+                       break;
+
+               case CLK_80:
+                       sd_card->sd_clock = CLK_60;
+                       break;
+
+               case CLK_60:
+                       sd_card->sd_clock = CLK_50;
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       retval = sd_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+int sd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 start_sector,
+       u16 sector_cnt)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       u32 data_addr;
+       u8 cfg2;
+       int retval;
+
+       if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+               RTSX_DEBUGP("sd_rw: Read %d %s from 0x%x\n", sector_cnt,
+                       (sector_cnt > 1) ? "sectors" : "sector", start_sector);
+       } else {
+               RTSX_DEBUGP("sd_rw: Write %d %s to 0x%x\n", sector_cnt,
+                       (sector_cnt > 1) ? "sectors" : "sector", start_sector);
+       }
+
+       sd_card->cleanup_counter = 0;
+
+       if (!(chip->card_ready & SD_CARD)) {
+               sd_card->seq_mode = 0;
+
+               retval = reset_sd_card(chip);
+               if (retval == STATUS_SUCCESS) {
+                       chip->card_ready |= SD_CARD;
+                       chip->card_fail &= ~SD_CARD;
+               } else {
+                       chip->card_ready &= ~SD_CARD;
+                       chip->card_fail |= SD_CARD;
+                       chip->capacity[chip->card2lun[SD_CARD]] = 0;
+                       chip->rw_need_retry = 1;
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       if (!CHK_SD_HCXC(sd_card) && !CHK_MMC_SECTOR_MODE(sd_card))
+               data_addr = start_sector << 9;
+       else
+               data_addr = start_sector;
+
+       sd_clr_err_code(chip);
+
+       retval = sd_switch_clock(chip);
+       if (retval != STATUS_SUCCESS) {
+               sd_set_err_code(chip, SD_IO_ERR);
+               TRACE_GOTO(chip, RW_FAIL);
+       }
+
+       if (sd_card->seq_mode &&
+               ((sd_card->pre_dir != srb->sc_data_direction) ||
+                       ((sd_card->pre_sec_addr + sd_card->pre_sec_cnt) !=
+                               start_sector))) {
+               if ((sd_card->pre_sec_cnt < 0x80)
+                               && (sd_card->pre_dir == DMA_FROM_DEVICE)
+                               && !CHK_SD30_SPEED(sd_card)
+                               && !CHK_SD_HS(sd_card)
+                               && !CHK_MMC_HS(sd_card)) {
+                       sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+                                       SD_RSP_TYPE_R1, NULL, 0);
+               }
+
+               retval = sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION,
+                               0, SD_RSP_TYPE_R1b, NULL, 0);
+               if (retval != STATUS_SUCCESS) {
+                       chip->rw_need_retry = 1;
+                       sd_set_err_code(chip, SD_STS_ERR);
+                       TRACE_GOTO(chip, RW_FAIL);
+               }
+
+               sd_card->seq_mode = 0;
+
+               retval = rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
+               if (retval != STATUS_SUCCESS) {
+                       sd_set_err_code(chip, SD_IO_ERR);
+                       TRACE_GOTO(chip, RW_FAIL);
+               }
+
+               if ((sd_card->pre_sec_cnt < 0x80)
+                               && !CHK_SD30_SPEED(sd_card)
+                               && !CHK_SD_HS(sd_card)
+                               && !CHK_MMC_HS(sd_card)) {
+                       sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+                                       SD_RSP_TYPE_R1, NULL, 0);
+               }
+       }
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, 0x00);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF, 0x02);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF,
+               (u8)sector_cnt);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF,
+               (u8)(sector_cnt >> 8));
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+
+       if (CHK_MMC_8BIT(sd_card))
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1,
+                       0x03, SD_BUS_WIDTH_8);
+       else if (CHK_MMC_4BIT(sd_card) || CHK_SD(sd_card))
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1,
+                       0x03, SD_BUS_WIDTH_4);
+       else
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1,
+                       0x03, SD_BUS_WIDTH_1);
+
+       if (sd_card->seq_mode) {
+               cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16|
+                       SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 |
+                       SD_RSP_LEN_0;
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, cfg2);
+
+               trans_dma_enable(srb->sc_data_direction, chip, sector_cnt * 512,
+                               DMA_512);
+
+               if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+                                    SD_TM_AUTO_READ_3 | SD_TRANSFER_START);
+               } else {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+                                    SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START);
+               }
+
+               rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+                       SD_TRANSFER_END, SD_TRANSFER_END);
+
+               rtsx_send_cmd_no_wait(chip);
+       } else {
+               if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+                       RTSX_DEBUGP("SD/MMC CMD %d\n", READ_MULTIPLE_BLOCK);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF,
+                                    0x40 | READ_MULTIPLE_BLOCK);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF,
+                               (u8)(data_addr >> 24));
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF,
+                               (u8)(data_addr >> 16));
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF,
+                               (u8)(data_addr >> 8));
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF,
+                               (u8)data_addr);
+
+                       cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+                               SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 |
+                               SD_RSP_LEN_6;
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
+                               cfg2);
+
+                       trans_dma_enable(srb->sc_data_direction, chip,
+                                       sector_cnt * 512, DMA_512);
+
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+                                    SD_TM_AUTO_READ_2 | SD_TRANSFER_START);
+                       rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+                                    SD_TRANSFER_END, SD_TRANSFER_END);
+
+                       rtsx_send_cmd_no_wait(chip);
+               } else {
+                       retval = rtsx_send_cmd(chip, SD_CARD, 50);
+                       if (retval < 0) {
+                               rtsx_clear_sd_error(chip);
+
+                               chip->rw_need_retry = 1;
+                               sd_set_err_code(chip, SD_TO_ERR);
+                               TRACE_GOTO(chip, RW_FAIL);
+                       }
+
+                       retval = wait_data_buf_ready(chip);
+                       if (retval != STATUS_SUCCESS) {
+                               chip->rw_need_retry = 1;
+                               sd_set_err_code(chip, SD_TO_ERR);
+                               TRACE_GOTO(chip, RW_FAIL);
+                       }
+
+                       retval = sd_send_cmd_get_rsp(chip, WRITE_MULTIPLE_BLOCK,
+                                       data_addr, SD_RSP_TYPE_R1, NULL, 0);
+                       if (retval != STATUS_SUCCESS) {
+                               chip->rw_need_retry = 1;
+                               TRACE_GOTO(chip, RW_FAIL);
+                       }
+
+                       rtsx_init_cmd(chip);
+
+                       cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+                               SD_NO_WAIT_BUSY_END |
+                               SD_NO_CHECK_CRC7 | SD_RSP_LEN_0;
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
+                               cfg2);
+
+                       trans_dma_enable(srb->sc_data_direction, chip,
+                                       sector_cnt * 512, DMA_512);
+
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+                                    SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START);
+                       rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+                                    SD_TRANSFER_END, SD_TRANSFER_END);
+
+                       rtsx_send_cmd_no_wait(chip);
+               }
+
+               sd_card->seq_mode = 1;
+       }
+
+       retval = rtsx_transfer_data(chip, SD_CARD, scsi_sglist(srb),
+                               scsi_bufflen(srb), scsi_sg_count(srb),
+                               srb->sc_data_direction, chip->sd_timeout);
+       if (retval < 0) {
+               u8 stat = 0;
+               int err;
+
+               sd_card->seq_mode = 0;
+
+               if (retval == -ETIMEDOUT)
+                       err = STATUS_TIMEDOUT;
+               else
+                       err = STATUS_FAIL;
+
+               rtsx_read_register(chip, REG_SD_STAT1, &stat);
+               rtsx_clear_sd_error(chip);
+               if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+                       chip->rw_need_retry = 0;
+                       RTSX_DEBUGP("No card exist, exit sd_rw\n");
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               chip->rw_need_retry = 1;
+
+               retval = sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION, 0,
+                                       SD_RSP_TYPE_R1b, NULL, 0);
+               if (retval != STATUS_SUCCESS) {
+                       sd_set_err_code(chip, SD_STS_ERR);
+                       TRACE_GOTO(chip, RW_FAIL);
+               }
+
+               if (stat & (SD_CRC7_ERR | SD_CRC16_ERR | SD_CRC_WRITE_ERR)) {
+                       RTSX_DEBUGP("SD CRC error, tune clock!\n");
+                       sd_set_err_code(chip, SD_CRC_ERR);
+                       TRACE_GOTO(chip, RW_FAIL);
+               }
+
+               if (err == STATUS_TIMEDOUT) {
+                       sd_set_err_code(chip, SD_TO_ERR);
+                       TRACE_GOTO(chip, RW_FAIL);
+               }
+
+               TRACE_RET(chip, err);
+       }
+
+       sd_card->pre_sec_addr = start_sector;
+       sd_card->pre_sec_cnt = sector_cnt;
+       sd_card->pre_dir = srb->sc_data_direction;
+
+       return STATUS_SUCCESS;
+
+RW_FAIL:
+       sd_card->seq_mode = 0;
+
+       if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+               chip->rw_need_retry = 0;
+               RTSX_DEBUGP("No card exist, exit sd_rw\n");
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (sd_check_err_code(chip, SD_CRC_ERR)) {
+               if (CHK_MMC_4BIT(sd_card) || CHK_MMC_8BIT(sd_card)) {
+                       sd_card->mmc_dont_switch_bus = 1;
+                       reset_mmc_only(chip);
+                       sd_card->mmc_dont_switch_bus = 0;
+               } else {
+                       sd_card->need_retune = 1;
+                       sd_auto_tune_clock(chip);
+               }
+       } else if (sd_check_err_code(chip, SD_TO_ERR | SD_STS_ERR)) {
+               retval = reset_sd_card(chip);
+               if (retval != STATUS_SUCCESS) {
+                       chip->card_ready &= ~SD_CARD;
+                       chip->card_fail |= SD_CARD;
+                       chip->capacity[chip->card2lun[SD_CARD]] = 0;
+               }
+       }
+
+       TRACE_RET(chip, STATUS_FAIL);
+}
+
+#ifdef SUPPORT_CPRM
+int soft_reset_sd_card(struct rtsx_chip *chip)
+{
+       return reset_sd(chip);
+}
+
+int ext_sd_send_cmd_get_rsp(struct rtsx_chip *chip, u8 cmd_idx,
+               u32 arg, u8 rsp_type, u8 *rsp, int rsp_len, int special_check)
+{
+       int retval;
+       int timeout = 100;
+       u16 reg_addr;
+       u8 *ptr;
+       int stat_idx = 0;
+       int rty_cnt = 0;
+
+       RTSX_DEBUGP("EXT SD/MMC CMD %d\n", cmd_idx);
+
+       if (rsp_type == SD_RSP_TYPE_R1b)
+               timeout = 3000;
+
+RTY_SEND_CMD:
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, 0x40 | cmd_idx);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF, (u8)(arg >> 24));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF, (u8)(arg >> 16));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF, (u8)(arg >> 8));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF, (u8)arg);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, rsp_type);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, PINGPONG_BUFFER);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER,
+                       0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END,
+               SD_TRANSFER_END);
+
+       if (rsp_type == SD_RSP_TYPE_R2) {
+               for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16;
+                    reg_addr++)
+                       rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
+
+               stat_idx = 17;
+       } else if (rsp_type != SD_RSP_TYPE_R0) {
+               for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4;
+                    reg_addr++)
+                       rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
+
+               stat_idx = 6;
+       }
+       rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_CMD5, 0, 0);
+
+       rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_STAT1, 0, 0);
+
+       retval = rtsx_send_cmd(chip, SD_CARD, timeout);
+       if (retval < 0) {
+               if (retval == -ETIMEDOUT) {
+                       rtsx_clear_sd_error(chip);
+
+                       if (rsp_type & SD_WAIT_BUSY_END) {
+                               retval = sd_check_data0_status(chip);
+                               if (retval != STATUS_SUCCESS)
+                                       TRACE_RET(chip, retval);
+                       } else {
+                               sd_set_err_code(chip, SD_TO_ERR);
+                       }
+               }
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (rsp_type == SD_RSP_TYPE_R0)
+               return STATUS_SUCCESS;
+
+       ptr = rtsx_get_cmd_data(chip) + 1;
+
+       if ((ptr[0] & 0xC0) != 0) {
+               sd_set_err_code(chip, SD_STS_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (!(rsp_type & SD_NO_CHECK_CRC7)) {
+               if (ptr[stat_idx] & SD_CRC7_ERR) {
+                       if (cmd_idx == WRITE_MULTIPLE_BLOCK) {
+                               sd_set_err_code(chip, SD_CRC_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       if (rty_cnt < SD_MAX_RETRY_COUNT) {
+                               wait_timeout(20);
+                               rty_cnt++;
+                               goto RTY_SEND_CMD;
+                       } else {
+                               sd_set_err_code(chip, SD_CRC_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+       }
+
+       if ((cmd_idx == SELECT_CARD) || (cmd_idx == APP_CMD) ||
+               (cmd_idx == SEND_STATUS) || (cmd_idx == STOP_TRANSMISSION)) {
+               if ((cmd_idx != STOP_TRANSMISSION) && (special_check == 0)) {
+                       if (ptr[1] & 0x80)
+                               TRACE_RET(chip, STATUS_FAIL);
+               }
+#ifdef SUPPORT_SD_LOCK
+               if (ptr[1] & 0x7D)
+#else
+               if (ptr[1] & 0x7F)
+#endif
+               {
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+               if (ptr[2] & 0xF8)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               if (cmd_idx == SELECT_CARD) {
+                       if (rsp_type == SD_RSP_TYPE_R2) {
+                               if ((ptr[3] & 0x1E) != 0x04)
+                                       TRACE_RET(chip, STATUS_FAIL);
+
+                       } else if (rsp_type == SD_RSP_TYPE_R0) {
+                               if ((ptr[3] & 0x1E) != 0x03)
+                                       TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+       }
+
+       if (rsp && rsp_len)
+               memcpy(rsp, ptr, rsp_len);
+
+       return STATUS_SUCCESS;
+}
+
+int ext_sd_get_rsp(struct rtsx_chip *chip, int len, u8 *rsp, u8 rsp_type)
+{
+       int retval, rsp_len;
+       u16 reg_addr;
+
+       if (rsp_type == SD_RSP_TYPE_R0)
+               return STATUS_SUCCESS;
+
+       rtsx_init_cmd(chip);
+
+       if (rsp_type == SD_RSP_TYPE_R2) {
+               for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16;
+                    reg_addr++)
+                       rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0xFF, 0);
+
+               rsp_len = 17;
+       } else if (rsp_type != SD_RSP_TYPE_R0) {
+               for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4;
+                    reg_addr++)
+                       rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0xFF, 0);
+
+               rsp_len = 6;
+       }
+       rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_CMD5, 0xFF, 0);
+
+       retval = rtsx_send_cmd(chip, SD_CARD, 100);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (rsp) {
+               int min_len = (rsp_len < len) ? rsp_len : len;
+
+               memcpy(rsp, rtsx_get_cmd_data(chip), min_len);
+
+               RTSX_DEBUGP("min_len = %d\n", min_len);
+               RTSX_DEBUGP("Response in cmd buf: 0x%x 0x%x 0x%x 0x%x\n",
+                       rsp[0], rsp[1], rsp[2], rsp[3]);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int sd_pass_thru_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       unsigned int lun = SCSI_LUN(srb);
+       int len;
+       u8 buf[18] = {
+               0x00,
+               0x00,
+               0x00,
+               0x0E,
+               0x00,
+               0x00,
+               0x00,
+               0x00,
+               0x53,
+               0x44,
+               0x20,
+               0x43,
+               0x61,
+               0x72,
+               0x64,
+               0x00,
+               0x00,
+               0x00,
+       };
+
+       sd_card->pre_cmd_err = 0;
+
+       if (!(CHK_BIT(chip->lun_mc, lun))) {
+               SET_BIT(chip->lun_mc, lun);
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if ((0x53 != srb->cmnd[2]) || (0x44 != srb->cmnd[3]) ||
+               (0x20 != srb->cmnd[4]) || (0x43 != srb->cmnd[5]) ||
+               (0x61 != srb->cmnd[6]) || (0x72 != srb->cmnd[7]) ||
+               (0x64 != srb->cmnd[8])) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       switch (srb->cmnd[1] & 0x0F) {
+       case 0:
+               sd_card->sd_pass_thru_en = 0;
+               break;
+
+       case 1:
+               sd_card->sd_pass_thru_en = 1;
+               break;
+
+       default:
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       buf[5] = (1 == CHK_SD(sd_card)) ?  0x01 : 0x02;
+       if (chip->card_wp & SD_CARD)
+               buf[5] |= 0x80;
+
+       buf[6] = (u8)(sd_card->sd_addr >> 16);
+       buf[7] = (u8)(sd_card->sd_addr >> 24);
+
+       buf[15] = chip->max_lun;
+
+       len = min_t(int, 18, scsi_bufflen(srb));
+       rtsx_stor_set_xfer_buf(buf, len, srb);
+
+       return TRANSPORT_GOOD;
+}
+
+static inline int get_rsp_type(struct scsi_cmnd *srb, u8 *rsp_type,
+                       int *rsp_len)
+{
+       if (!rsp_type || !rsp_len)
+               return STATUS_FAIL;
+
+       switch (srb->cmnd[10]) {
+       case 0x03:
+               *rsp_type = SD_RSP_TYPE_R0;
+               *rsp_len = 0;
+               break;
+
+       case 0x04:
+               *rsp_type = SD_RSP_TYPE_R1;
+               *rsp_len = 6;
+               break;
+
+       case 0x05:
+               *rsp_type = SD_RSP_TYPE_R1b;
+               *rsp_len = 6;
+               break;
+
+       case 0x06:
+               *rsp_type = SD_RSP_TYPE_R2;
+               *rsp_len = 17;
+               break;
+
+       case 0x07:
+               *rsp_type = SD_RSP_TYPE_R3;
+               *rsp_len = 6;
+               break;
+
+       default:
+               return STATUS_FAIL;
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int sd_execute_no_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       unsigned int lun = SCSI_LUN(srb);
+       int retval, rsp_len;
+       u8 cmd_idx, rsp_type;
+       u8 standby = 0, acmd = 0;
+       u32 arg;
+
+       if (!sd_card->sd_pass_thru_en) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       retval = sd_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, TRANSPORT_FAILED);
+
+       if (sd_card->pre_cmd_err) {
+               sd_card->pre_cmd_err = 0;
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       cmd_idx = srb->cmnd[2] & 0x3F;
+       if (srb->cmnd[1] & 0x02)
+               standby = 1;
+
+       if (srb->cmnd[1] & 0x01)
+               acmd = 1;
+
+       arg = ((u32)srb->cmnd[3] << 24) | ((u32)srb->cmnd[4] << 16) |
+               ((u32)srb->cmnd[5] << 8) | srb->cmnd[6];
+
+       retval = get_rsp_type(srb, &rsp_type, &rsp_len);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+       sd_card->last_rsp_type = rsp_type;
+
+       retval = sd_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, TRANSPORT_FAILED);
+
+#ifdef SUPPORT_SD_LOCK
+       if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) {
+               if (CHK_MMC_8BIT(sd_card)) {
+                       retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03,
+                                               SD_BUS_WIDTH_8);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+
+               } else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) {
+                       retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03,
+                                               SD_BUS_WIDTH_4);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+#else
+       retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_4);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, TRANSPORT_FAILED);
+#endif
+
+       if (standby) {
+               retval = sd_select_card(chip, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Cmd_Failed);
+       }
+
+       if (acmd) {
+               retval = ext_sd_send_cmd_get_rsp(chip, APP_CMD,
+                                               sd_card->sd_addr,
+                                               SD_RSP_TYPE_R1, NULL, 0, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Cmd_Failed);
+       }
+
+       retval = ext_sd_send_cmd_get_rsp(chip, cmd_idx, arg, rsp_type,
+                       sd_card->rsp, rsp_len, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_GOTO(chip, SD_Execute_Cmd_Failed);
+
+       if (standby) {
+               retval = sd_select_card(chip, 1);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Cmd_Failed);
+       }
+
+#ifdef SUPPORT_SD_LOCK
+       retval = sd_update_lock_status(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_GOTO(chip, SD_Execute_Cmd_Failed);
+#endif
+
+       scsi_set_resid(srb, 0);
+       return TRANSPORT_GOOD;
+
+SD_Execute_Cmd_Failed:
+       sd_card->pre_cmd_err = 1;
+       set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
+       release_sd_card(chip);
+       do_reset_sd_card(chip);
+       if (!(chip->card_ready & SD_CARD))
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+
+       TRACE_RET(chip, TRANSPORT_FAILED);
+}
+
+int sd_execute_read_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       unsigned int lun = SCSI_LUN(srb);
+       int retval, rsp_len, i;
+       int cmd13_checkbit = 0, read_err = 0;
+       u8 cmd_idx, rsp_type, bus_width;
+       u8 send_cmd12 = 0, standby = 0, acmd = 0;
+       u32 data_len;
+
+       if (!sd_card->sd_pass_thru_en) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (sd_card->pre_cmd_err) {
+               sd_card->pre_cmd_err = 0;
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       retval = sd_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, TRANSPORT_FAILED);
+
+       cmd_idx = srb->cmnd[2] & 0x3F;
+       if (srb->cmnd[1] & 0x04)
+               send_cmd12 = 1;
+
+       if (srb->cmnd[1] & 0x02)
+               standby = 1;
+
+       if (srb->cmnd[1] & 0x01)
+               acmd = 1;
+
+       data_len = ((u32)srb->cmnd[7] << 16) | ((u32)srb->cmnd[8]
+                                               << 8) | srb->cmnd[9];
+
+       retval = get_rsp_type(srb, &rsp_type, &rsp_len);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+       sd_card->last_rsp_type = rsp_type;
+
+       retval = sd_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, TRANSPORT_FAILED);
+
+#ifdef SUPPORT_SD_LOCK
+       if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) {
+               if (CHK_MMC_8BIT(sd_card))
+                       bus_width = SD_BUS_WIDTH_8;
+               else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card))
+                       bus_width = SD_BUS_WIDTH_4;
+               else
+                       bus_width = SD_BUS_WIDTH_1;
+       } else {
+               bus_width = SD_BUS_WIDTH_4;
+       }
+       RTSX_DEBUGP("bus_width = %d\n", bus_width);
+#else
+       bus_width = SD_BUS_WIDTH_4;
+#endif
+
+       if (data_len < 512) {
+               retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, data_len,
+                               SD_RSP_TYPE_R1, NULL, 0, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+       }
+
+       if (standby) {
+               retval = sd_select_card(chip, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+       }
+
+       if (acmd) {
+               retval = ext_sd_send_cmd_get_rsp(chip, APP_CMD,
+                                               sd_card->sd_addr,
+                                               SD_RSP_TYPE_R1, NULL, 0, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+       }
+
+       if (data_len <= 512) {
+               int min_len;
+               u8 *buf;
+               u16 byte_cnt, blk_cnt;
+               u8 cmd[5];
+
+               byte_cnt = ((u16)(srb->cmnd[8] & 0x03) << 8) | srb->cmnd[9];
+               blk_cnt = 1;
+
+               cmd[0] = 0x40 | cmd_idx;
+               cmd[1] = srb->cmnd[3];
+               cmd[2] = srb->cmnd[4];
+               cmd[3] = srb->cmnd[5];
+               cmd[4] = srb->cmnd[6];
+
+               buf = kmalloc(data_len, GFP_KERNEL);
+               if (buf == NULL)
+                       TRACE_RET(chip, TRANSPORT_ERROR);
+
+               retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, byte_cnt,
+                                      blk_cnt, bus_width, buf, data_len, 2000);
+               if (retval != STATUS_SUCCESS) {
+                       read_err = 1;
+                       kfree(buf);
+                       rtsx_clear_sd_error(chip);
+                       TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+               }
+
+               min_len = min(data_len, scsi_bufflen(srb));
+               rtsx_stor_set_xfer_buf(buf, min_len, srb);
+
+               kfree(buf);
+       } else if (!(data_len & 0x1FF)) {
+               rtsx_init_cmd(chip);
+
+               trans_dma_enable(DMA_FROM_DEVICE, chip, data_len, DMA_512);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF,
+                       0x02);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF,
+                       0x00);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H,
+                               0xFF, (srb->cmnd[7] & 0xFE) >> 1);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L,
+                               0xFF, (u8)((data_len & 0x0001FE00) >> 9));
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF,
+                       0x40 | cmd_idx);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF,
+                       srb->cmnd[3]);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF,
+                       srb->cmnd[4]);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF,
+                       srb->cmnd[5]);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF,
+                       srb->cmnd[6]);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, 0x03, bus_width);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, rsp_type);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER,
+                            0xFF, SD_TM_AUTO_READ_2 | SD_TRANSFER_START);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+                       SD_TRANSFER_END, SD_TRANSFER_END);
+
+               rtsx_send_cmd_no_wait(chip);
+
+               retval = rtsx_transfer_data(chip, SD_CARD, scsi_sglist(srb),
+                                       scsi_bufflen(srb), scsi_sg_count(srb),
+                                       DMA_FROM_DEVICE, 10000);
+               if (retval < 0) {
+                       read_err = 1;
+                       rtsx_clear_sd_error(chip);
+                       TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+               }
+
+       } else {
+               TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+       }
+
+       retval = ext_sd_get_rsp(chip, rsp_len, sd_card->rsp, rsp_type);
+       if (retval != STATUS_SUCCESS)
+               TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+
+       if (standby) {
+               retval = sd_select_card(chip, 1);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+       }
+
+       if (send_cmd12) {
+               retval = ext_sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION,
+                               0, SD_RSP_TYPE_R1b, NULL, 0, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+       }
+
+       if (data_len < 512) {
+               retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200,
+                               SD_RSP_TYPE_R1, NULL, 0, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+
+               retval = rtsx_write_register(chip, SD_BYTE_CNT_H, 0xFF, 0x02);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+
+               retval = rtsx_write_register(chip, SD_BYTE_CNT_L, 0xFF, 0x00);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+       }
+
+       if ((srb->cmnd[1] & 0x02) || (srb->cmnd[1] & 0x04))
+               cmd13_checkbit = 1;
+
+       for (i = 0; i < 3; i++) {
+               retval = ext_sd_send_cmd_get_rsp(chip, SEND_STATUS,
+                                               sd_card->sd_addr,
+                                               SD_RSP_TYPE_R1, NULL, 0,
+                                               cmd13_checkbit);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (retval != STATUS_SUCCESS)
+               TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
+
+       scsi_set_resid(srb, 0);
+       return TRANSPORT_GOOD;
+
+SD_Execute_Read_Cmd_Failed:
+       sd_card->pre_cmd_err = 1;
+       set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
+       if (read_err)
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+
+       release_sd_card(chip);
+       do_reset_sd_card(chip);
+       if (!(chip->card_ready & SD_CARD))
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+
+       TRACE_RET(chip, TRANSPORT_FAILED);
+}
+
+int sd_execute_write_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       unsigned int lun = SCSI_LUN(srb);
+       int retval, rsp_len, i;
+       int cmd13_checkbit = 0, write_err = 0;
+       u8 cmd_idx, rsp_type;
+       u8 send_cmd12 = 0, standby = 0, acmd = 0;
+       u32 data_len, arg;
+#ifdef SUPPORT_SD_LOCK
+       int lock_cmd_fail = 0;
+       u8 sd_lock_state = 0;
+       u8 lock_cmd_type = 0;
+#endif
+
+       if (!sd_card->sd_pass_thru_en) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (sd_card->pre_cmd_err) {
+               sd_card->pre_cmd_err = 0;
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       retval = sd_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, TRANSPORT_FAILED);
+
+       cmd_idx = srb->cmnd[2] & 0x3F;
+       if (srb->cmnd[1] & 0x04)
+               send_cmd12 = 1;
+
+       if (srb->cmnd[1] & 0x02)
+               standby = 1;
+
+       if (srb->cmnd[1] & 0x01)
+               acmd = 1;
+
+       data_len = ((u32)srb->cmnd[7] << 16) | ((u32)srb->cmnd[8]
+                                               << 8) | srb->cmnd[9];
+       arg = ((u32)srb->cmnd[3] << 24) | ((u32)srb->cmnd[4] << 16) |
+               ((u32)srb->cmnd[5] << 8) | srb->cmnd[6];
+
+#ifdef SUPPORT_SD_LOCK
+       if (cmd_idx == LOCK_UNLOCK) {
+               sd_lock_state = sd_card->sd_lock_status;
+               sd_lock_state &= SD_LOCKED;
+       }
+#endif
+
+       retval = get_rsp_type(srb, &rsp_type, &rsp_len);
+       if (retval != STATUS_SUCCESS) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+       sd_card->last_rsp_type = rsp_type;
+
+       retval = sd_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, TRANSPORT_FAILED);
+
+#ifdef SUPPORT_SD_LOCK
+       if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) {
+               if (CHK_MMC_8BIT(sd_card)) {
+                       retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03,
+                                               SD_BUS_WIDTH_8);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+
+               } else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) {
+                       retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03,
+                                               SD_BUS_WIDTH_4);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+       }
+#else
+       retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_4);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, TRANSPORT_FAILED);
+#endif
+
+       if (data_len < 512) {
+               retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, data_len,
+                               SD_RSP_TYPE_R1, NULL, 0, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+       }
+
+       if (standby) {
+               retval = sd_select_card(chip, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+       }
+
+       if (acmd) {
+               retval = ext_sd_send_cmd_get_rsp(chip, APP_CMD,
+                                               sd_card->sd_addr,
+                                               SD_RSP_TYPE_R1, NULL, 0, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+       }
+
+       retval = ext_sd_send_cmd_get_rsp(chip, cmd_idx, arg, rsp_type,
+                       sd_card->rsp, rsp_len, 0);
+       if (retval != STATUS_SUCCESS)
+               TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+
+       if (data_len <= 512) {
+               u16 i;
+               u8 *buf;
+
+               buf = kmalloc(data_len, GFP_KERNEL);
+               if (buf == NULL)
+                       TRACE_RET(chip, TRANSPORT_ERROR);
+
+               rtsx_stor_get_xfer_buf(buf, data_len, srb);
+
+#ifdef SUPPORT_SD_LOCK
+               if (cmd_idx == LOCK_UNLOCK)
+                       lock_cmd_type = buf[0] & 0x0F;
+#endif
+
+               if (data_len > 256) {
+                       rtsx_init_cmd(chip);
+                       for (i = 0; i < 256; i++) {
+                               rtsx_add_cmd(chip, WRITE_REG_CMD,
+                                               PPBUF_BASE2 + i, 0xFF, buf[i]);
+                       }
+                       retval = rtsx_send_cmd(chip, 0, 250);
+                       if (retval != STATUS_SUCCESS) {
+                               kfree(buf);
+                               TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+                       }
+
+                       rtsx_init_cmd(chip);
+                       for (i = 256; i < data_len; i++) {
+                               rtsx_add_cmd(chip, WRITE_REG_CMD,
+                                               PPBUF_BASE2 + i, 0xFF, buf[i]);
+                       }
+                       retval = rtsx_send_cmd(chip, 0, 250);
+                       if (retval != STATUS_SUCCESS) {
+                               kfree(buf);
+                               TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+                       }
+               } else {
+                       rtsx_init_cmd(chip);
+                       for (i = 0; i < data_len; i++) {
+                               rtsx_add_cmd(chip, WRITE_REG_CMD,
+                                               PPBUF_BASE2 + i, 0xFF, buf[i]);
+                       }
+                       retval = rtsx_send_cmd(chip, 0, 250);
+                       if (retval != STATUS_SUCCESS) {
+                               kfree(buf);
+                               TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+                       }
+               }
+
+               kfree(buf);
+
+               rtsx_init_cmd(chip);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF,
+                       srb->cmnd[8] & 0x03);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF,
+                       srb->cmnd[9]);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF,
+                       0x00);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF,
+                       0x01);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01,
+                       PINGPONG_BUFFER);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+                            SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+                       SD_TRANSFER_END, SD_TRANSFER_END);
+
+               retval = rtsx_send_cmd(chip, SD_CARD, 250);
+       } else if (!(data_len & 0x1FF)) {
+               rtsx_init_cmd(chip);
+
+               trans_dma_enable(DMA_TO_DEVICE, chip, data_len, DMA_512);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF,
+                       0x02);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF,
+                       0x00);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H,
+                               0xFF, (srb->cmnd[7] & 0xFE) >> 1);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L,
+                               0xFF, (u8)((data_len & 0x0001FE00) >> 9));
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+                       SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+                       SD_TRANSFER_END, SD_TRANSFER_END);
+
+               rtsx_send_cmd_no_wait(chip);
+
+               retval = rtsx_transfer_data(chip, SD_CARD, scsi_sglist(srb),
+                                       scsi_bufflen(srb), scsi_sg_count(srb),
+                                       DMA_TO_DEVICE, 10000);
+
+       } else {
+               TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+       }
+
+       if (retval < 0) {
+               write_err = 1;
+               rtsx_clear_sd_error(chip);
+               TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+       }
+
+#ifdef SUPPORT_SD_LOCK
+       if (cmd_idx == LOCK_UNLOCK) {
+               if (lock_cmd_type == SD_ERASE) {
+                       sd_card->sd_erase_status = SD_UNDER_ERASING;
+                       scsi_set_resid(srb, 0);
+                       return TRANSPORT_GOOD;
+               }
+
+               rtsx_init_cmd(chip);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, 0xFD30, 0x02, 0x02);
+
+               rtsx_send_cmd(chip, SD_CARD, 250);
+
+               retval = sd_update_lock_status(chip);
+               if (retval != STATUS_SUCCESS) {
+                       RTSX_DEBUGP("Lock command fail!\n");
+                       lock_cmd_fail = 1;
+               }
+       }
+#endif /* SUPPORT_SD_LOCK */
+
+       if (standby) {
+               retval = sd_select_card(chip, 1);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+       }
+
+       if (send_cmd12) {
+               retval = ext_sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION,
+                               0, SD_RSP_TYPE_R1b, NULL, 0, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+       }
+
+       if (data_len < 512) {
+               retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200,
+                               SD_RSP_TYPE_R1, NULL, 0, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+
+               retval = rtsx_write_register(chip, SD_BYTE_CNT_H, 0xFF, 0x02);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+
+               rtsx_write_register(chip, SD_BYTE_CNT_L, 0xFF, 0x00);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+       }
+
+       if ((srb->cmnd[1] & 0x02) || (srb->cmnd[1] & 0x04))
+               cmd13_checkbit = 1;
+
+       for (i = 0; i < 3; i++) {
+               retval = ext_sd_send_cmd_get_rsp(chip, SEND_STATUS,
+                                               sd_card->sd_addr,
+                                               SD_RSP_TYPE_R1, NULL, 0,
+                                               cmd13_checkbit);
+               if (retval == STATUS_SUCCESS)
+                       break;
+       }
+       if (retval != STATUS_SUCCESS)
+               TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+
+#ifdef SUPPORT_SD_LOCK
+       if (cmd_idx == LOCK_UNLOCK) {
+               if (!lock_cmd_fail) {
+                       RTSX_DEBUGP("lock_cmd_type = 0x%x\n", lock_cmd_type);
+                       if (lock_cmd_type & SD_CLR_PWD)
+                               sd_card->sd_lock_status &= ~SD_PWD_EXIST;
+
+                       if (lock_cmd_type & SD_SET_PWD)
+                               sd_card->sd_lock_status |= SD_PWD_EXIST;
+               }
+
+               RTSX_DEBUGP("sd_lock_state = 0x%x, sd_card->sd_lock_status = 0x%x\n",
+                            sd_lock_state, sd_card->sd_lock_status);
+               if (sd_lock_state ^ (sd_card->sd_lock_status & SD_LOCKED)) {
+                       sd_card->sd_lock_notify = 1;
+                       if (sd_lock_state) {
+                               if (sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) {
+                                       sd_card->sd_lock_status |= (
+                                               SD_UNLOCK_POW_ON | SD_SDR_RST);
+                                       if (CHK_SD(sd_card)) {
+                                               retval = reset_sd(chip);
+                                               if (retval != STATUS_SUCCESS) {
+                                                       sd_card->sd_lock_status &= ~(SD_UNLOCK_POW_ON | SD_SDR_RST);
+                                                       TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
+                                               }
+                                       }
+
+                                       sd_card->sd_lock_status &= ~(SD_UNLOCK_POW_ON | SD_SDR_RST);
+                               }
+                       }
+               }
+       }
+
+       if (lock_cmd_fail) {
+               scsi_set_resid(srb, 0);
+               set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+#endif  /* SUPPORT_SD_LOCK */
+
+       scsi_set_resid(srb, 0);
+       return TRANSPORT_GOOD;
+
+SD_Execute_Write_Cmd_Failed:
+       sd_card->pre_cmd_err = 1;
+       set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
+       if (write_err)
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
+
+       release_sd_card(chip);
+       do_reset_sd_card(chip);
+       if (!(chip->card_ready & SD_CARD))
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+
+       TRACE_RET(chip, TRANSPORT_FAILED);
+}
+
+int sd_get_cmd_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       unsigned int lun = SCSI_LUN(srb);
+       int count;
+       u16 data_len;
+
+       if (!sd_card->sd_pass_thru_en) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (sd_card->pre_cmd_err) {
+               sd_card->pre_cmd_err = 0;
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       data_len = ((u16)srb->cmnd[7] << 8) | srb->cmnd[8];
+
+       if (sd_card->last_rsp_type == SD_RSP_TYPE_R0) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       } else if (sd_card->last_rsp_type == SD_RSP_TYPE_R2) {
+               count = (data_len < 17) ? data_len : 17;
+       } else {
+               count = (data_len < 6) ? data_len : 6;
+       }
+       rtsx_stor_set_xfer_buf(sd_card->rsp, count, srb);
+
+       RTSX_DEBUGP("Response length: %d\n", data_len);
+       RTSX_DEBUGP("Response: 0x%x 0x%x 0x%x 0x%x\n", sd_card->rsp[0],
+               sd_card->rsp[1], sd_card->rsp[2], sd_card->rsp[3]);
+
+       scsi_set_resid(srb, 0);
+       return TRANSPORT_GOOD;
+}
+
+int sd_hw_rst(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       unsigned int lun = SCSI_LUN(srb);
+       int retval;
+
+       if (!sd_card->sd_pass_thru_en) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if (sd_card->pre_cmd_err) {
+               sd_card->pre_cmd_err = 0;
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       if ((0x53 != srb->cmnd[2]) || (0x44 != srb->cmnd[3]) ||
+               (0x20 != srb->cmnd[4]) || (0x43 != srb->cmnd[5]) ||
+               (0x61 != srb->cmnd[6]) || (0x72 != srb->cmnd[7]) ||
+               (0x64 != srb->cmnd[8])) {
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       switch (srb->cmnd[1] & 0x0F) {
+       case 0:
+#ifdef SUPPORT_SD_LOCK
+               if (0x64 == srb->cmnd[9])
+                       sd_card->sd_lock_status |= SD_SDR_RST;
+#endif
+               retval = reset_sd_card(chip);
+               if (retval != STATUS_SUCCESS) {
+#ifdef SUPPORT_SD_LOCK
+                       sd_card->sd_lock_status &= ~SD_SDR_RST;
+#endif
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+                       sd_card->pre_cmd_err = 1;
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+#ifdef SUPPORT_SD_LOCK
+               sd_card->sd_lock_status &= ~SD_SDR_RST;
+#endif
+               break;
+
+       case 1:
+               retval = soft_reset_sd_card(chip);
+               if (retval != STATUS_SUCCESS) {
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+                       sd_card->pre_cmd_err = 1;
+                       TRACE_RET(chip, TRANSPORT_FAILED);
+               }
+               break;
+
+       default:
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+               TRACE_RET(chip, TRANSPORT_FAILED);
+       }
+
+       scsi_set_resid(srb, 0);
+       return TRANSPORT_GOOD;
+}
+#endif
+
+void sd_cleanup_work(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+
+       if (sd_card->seq_mode) {
+               RTSX_DEBUGP("SD: stop transmission\n");
+               sd_stop_seq_mode(chip);
+               sd_card->cleanup_counter = 0;
+       }
+}
+
+int sd_power_off_card3v3(struct rtsx_chip *chip)
+{
+       int retval;
+
+       retval = disable_card_clock(chip, SD_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_WRITE_REG(chip, CARD_OE, SD_OUTPUT_EN, 0);
+
+       if (!chip->ft2_fast_mode) {
+               retval = card_power_off(chip, SD_CARD);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               wait_timeout(50);
+       }
+
+       if (chip->asic_code) {
+               retval = sd_pull_ctl_disable(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       } else {
+               RTSX_WRITE_REG(chip, FPGA_PULL_CTL,
+                       FPGA_SD_PULL_CTL_BIT | 0x20, FPGA_SD_PULL_CTL_BIT);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int release_sd_card(struct rtsx_chip *chip)
+{
+       struct sd_info *sd_card = &(chip->sd_card);
+       int retval;
+
+       RTSX_DEBUGP("release_sd_card\n");
+
+       chip->card_ready &= ~SD_CARD;
+       chip->card_fail &= ~SD_CARD;
+       chip->card_wp &= ~SD_CARD;
+
+       chip->sd_io = 0;
+       chip->sd_int = 0;
+
+#ifdef SUPPORT_SD_LOCK
+       sd_card->sd_lock_status = 0;
+       sd_card->sd_erase_status = 0;
+#endif
+
+       memset(sd_card->raw_csd, 0, 16);
+       memset(sd_card->raw_scr, 0, 8);
+
+       retval = sd_power_off_card3v3(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
diff --git a/drivers/staging/rts5208/sd.h b/drivers/staging/rts5208/sd.h
new file mode 100644 (file)
index 0000000..735b2d0
--- /dev/null
@@ -0,0 +1,301 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_SD_H
+#define __REALTEK_RTSX_SD_H
+
+#include "rtsx_chip.h"
+
+#define SUPPORT_VOLTAGE        0x003C0000
+
+/* Error Code */
+#define        SD_NO_ERROR             0x0
+#define        SD_CRC_ERR              0x80
+#define        SD_TO_ERR               0x40
+#define        SD_NO_CARD              0x20
+#define SD_BUSY                        0x10
+#define        SD_STS_ERR              0x08
+#define SD_RSP_TIMEOUT         0x04
+#define SD_IO_ERR              0x02
+
+/* Return code for MMC switch bus */
+#define SWITCH_SUCCESS         0
+#define SWITCH_ERR             1
+#define SWITCH_FAIL            2
+
+/* MMC/SD Command Index */
+/* Basic command (class 0) */
+#define GO_IDLE_STATE          0
+#define        SEND_OP_COND            1
+#define        ALL_SEND_CID            2
+#define        SET_RELATIVE_ADDR       3
+#define        SEND_RELATIVE_ADDR      3
+#define        SET_DSR                 4
+#define IO_SEND_OP_COND                5
+#define        SWITCH                  6
+#define        SELECT_CARD             7
+#define        DESELECT_CARD           7
+/* CMD8 is "SEND_EXT_CSD" for MMC4.x Spec
+ * while is "SEND_IF_COND" for SD 2.0
+ */
+#define        SEND_EXT_CSD            8
+#define        SEND_IF_COND            8
+
+#define        SEND_CSD                9
+#define        SEND_CID                10
+#define        VOLTAGE_SWITCH          11
+#define        READ_DAT_UTIL_STOP      11
+#define        STOP_TRANSMISSION       12
+#define        SEND_STATUS             13
+#define        GO_INACTIVE_STATE       15
+
+#define        SET_BLOCKLEN            16
+#define        READ_SINGLE_BLOCK       17
+#define        READ_MULTIPLE_BLOCK     18
+#define        SEND_TUNING_PATTERN     19
+
+#define        BUSTEST_R               14
+#define        BUSTEST_W               19
+
+#define        WRITE_BLOCK             24
+#define        WRITE_MULTIPLE_BLOCK    25
+#define        PROGRAM_CSD             27
+
+#define        ERASE_WR_BLK_START      32
+#define        ERASE_WR_BLK_END        33
+#define        ERASE_CMD               38
+
+#define LOCK_UNLOCK            42
+#define        IO_RW_DIRECT            52
+
+#define        APP_CMD                 55
+#define        GEN_CMD                 56
+
+#define        SET_BUS_WIDTH           6
+#define        SD_STATUS               13
+#define        SEND_NUM_WR_BLOCKS      22
+#define        SET_WR_BLK_ERASE_COUNT  23
+#define        SD_APP_OP_COND          41
+#define        SET_CLR_CARD_DETECT     42
+#define        SEND_SCR                51
+
+#define        SD_READ_COMPLETE        0x00
+#define        SD_READ_TO              0x01
+#define        SD_READ_ADVENCE         0x02
+
+#define        SD_CHECK_MODE           0x00
+#define        SD_SWITCH_MODE          0x80
+#define        SD_FUNC_GROUP_1         0x01
+#define        SD_FUNC_GROUP_2         0x02
+#define        SD_FUNC_GROUP_3         0x03
+#define        SD_FUNC_GROUP_4         0x04
+#define        SD_CHECK_SPEC_V1_1      0xFF
+
+#define        NO_ARGUMENT                             0x00
+#define        CHECK_PATTERN                           0x000000AA
+#define        VOLTAGE_SUPPLY_RANGE                    0x00000100
+#define        SUPPORT_HIGH_AND_EXTENDED_CAPACITY      0x40000000
+#define        SUPPORT_MAX_POWER_PERMANCE              0x10000000
+#define        SUPPORT_1V8                             0x01000000
+
+#define        SWTICH_NO_ERR           0x00
+#define        CARD_NOT_EXIST          0x01
+#define        SPEC_NOT_SUPPORT        0x02
+#define        CHECK_MODE_ERR          0x03
+#define        CHECK_NOT_READY         0x04
+#define        SWITCH_CRC_ERR          0x05
+#define        SWITCH_MODE_ERR         0x06
+#define        SWITCH_PASS             0x07
+
+#ifdef SUPPORT_SD_LOCK
+#define SD_ERASE               0x08
+#define SD_LOCK                        0x04
+#define SD_UNLOCK              0x00
+#define SD_CLR_PWD             0x02
+#define SD_SET_PWD             0x01
+
+#define SD_PWD_LEN             0x10
+
+#define SD_LOCKED              0x80
+#define SD_LOCK_1BIT_MODE      0x40
+#define SD_PWD_EXIST           0x20
+#define SD_UNLOCK_POW_ON       0x01
+#define SD_SDR_RST             0x02
+
+#define SD_NOT_ERASE           0x00
+#define SD_UNDER_ERASING       0x01
+#define SD_COMPLETE_ERASE      0x02
+
+#define SD_RW_FORBIDDEN                0x0F
+
+#endif
+
+#define        HS_SUPPORT                      0x01
+#define        SDR50_SUPPORT                   0x02
+#define        SDR104_SUPPORT                  0x03
+#define        DDR50_SUPPORT                   0x04
+
+#define        HS_SUPPORT_MASK                 0x02
+#define        SDR50_SUPPORT_MASK              0x04
+#define        SDR104_SUPPORT_MASK             0x08
+#define        DDR50_SUPPORT_MASK              0x10
+
+#define        HS_QUERY_SWITCH_OK              0x01
+#define        SDR50_QUERY_SWITCH_OK           0x02
+#define        SDR104_QUERY_SWITCH_OK          0x03
+#define        DDR50_QUERY_SWITCH_OK           0x04
+
+#define        HS_SWITCH_BUSY                  0x02
+#define        SDR50_SWITCH_BUSY               0x04
+#define        SDR104_SWITCH_BUSY              0x08
+#define        DDR50_SWITCH_BUSY               0x10
+
+#define        FUNCTION_GROUP1_SUPPORT_OFFSET       0x0D
+#define FUNCTION_GROUP1_QUERY_SWITCH_OFFSET  0x10
+#define FUNCTION_GROUP1_CHECK_BUSY_OFFSET    0x1D
+
+#define        DRIVING_TYPE_A          0x01
+#define        DRIVING_TYPE_B              0x00
+#define        DRIVING_TYPE_C              0x02
+#define        DRIVING_TYPE_D          0x03
+
+#define        DRIVING_TYPE_A_MASK         0x02
+#define        DRIVING_TYPE_B_MASK         0x01
+#define        DRIVING_TYPE_C_MASK         0x04
+#define        DRIVING_TYPE_D_MASK         0x08
+
+#define        TYPE_A_QUERY_SWITCH_OK  0x01
+#define        TYPE_B_QUERY_SWITCH_OK  0x00
+#define        TYPE_C_QUERY_SWITCH_OK  0x02
+#define        TYPE_D_QUERY_SWITCH_OK  0x03
+
+#define        TYPE_A_SWITCH_BUSY          0x02
+#define        TYPE_B_SWITCH_BUSY          0x01
+#define        TYPE_C_SWITCH_BUSY      0x04
+#define        TYPE_D_SWITCH_BUSY      0x08
+
+#define        FUNCTION_GROUP3_SUPPORT_OFFSET       0x09
+#define FUNCTION_GROUP3_QUERY_SWITCH_OFFSET  0x0F
+#define FUNCTION_GROUP3_CHECK_BUSY_OFFSET    0x19
+
+#define        CURRENT_LIMIT_200           0x00
+#define        CURRENT_LIMIT_400           0x01
+#define        CURRENT_LIMIT_600           0x02
+#define        CURRENT_LIMIT_800           0x03
+
+#define        CURRENT_LIMIT_200_MASK  0x01
+#define        CURRENT_LIMIT_400_MASK  0x02
+#define        CURRENT_LIMIT_600_MASK  0x04
+#define        CURRENT_LIMIT_800_MASK  0x08
+
+#define        CURRENT_LIMIT_200_QUERY_SWITCH_OK    0x00
+#define        CURRENT_LIMIT_400_QUERY_SWITCH_OK    0x01
+#define        CURRENT_LIMIT_600_QUERY_SWITCH_OK    0x02
+#define        CURRENT_LIMIT_800_QUERY_SWITCH_OK    0x03
+
+#define        CURRENT_LIMIT_200_SWITCH_BUSY        0x01
+#define        CURRENT_LIMIT_400_SWITCH_BUSY        0x02
+#define        CURRENT_LIMIT_600_SWITCH_BUSY        0x04
+#define        CURRENT_LIMIT_800_SWITCH_BUSY        0x08
+
+#define        FUNCTION_GROUP4_SUPPORT_OFFSET       0x07
+#define FUNCTION_GROUP4_QUERY_SWITCH_OFFSET  0x0F
+#define FUNCTION_GROUP4_CHECK_BUSY_OFFSET    0x17
+
+#define        DATA_STRUCTURE_VER_OFFSET       0x11
+
+#define MAX_PHASE                      31
+
+#define MMC_8BIT_BUS                   0x0010
+#define MMC_4BIT_BUS                   0x0020
+
+#define MMC_SWITCH_ERR                 0x80
+
+#define SD_IO_3V3              0
+#define SD_IO_1V8              1
+
+#define TUNE_TX    0x00
+#define TUNE_RX           0x01
+
+#define CHANGE_TX  0x00
+#define CHANGE_RX  0x01
+
+#define DCM_HIGH_FREQUENCY_MODE  0x00
+#define DCM_LOW_FREQUENCY_MODE   0x01
+
+#define DCM_HIGH_FREQUENCY_MODE_SET  0x0C
+#define DCM_Low_FREQUENCY_MODE_SET   0x00
+
+#define MULTIPLY_BY_1    0x00
+#define MULTIPLY_BY_2    0x01
+#define MULTIPLY_BY_3    0x02
+#define MULTIPLY_BY_4    0x03
+#define MULTIPLY_BY_5    0x04
+#define MULTIPLY_BY_6    0x05
+#define MULTIPLY_BY_7    0x06
+#define MULTIPLY_BY_8    0x07
+#define MULTIPLY_BY_9    0x08
+#define MULTIPLY_BY_10   0x09
+
+#define DIVIDE_BY_2      0x01
+#define DIVIDE_BY_3      0x02
+#define DIVIDE_BY_4      0x03
+#define DIVIDE_BY_5      0x04
+#define DIVIDE_BY_6      0x05
+#define DIVIDE_BY_7      0x06
+#define DIVIDE_BY_8      0x07
+#define DIVIDE_BY_9      0x08
+#define DIVIDE_BY_10     0x09
+
+struct timing_phase_path {
+       int start;
+       int end;
+       int mid;
+       int len;
+};
+
+int sd_select_card(struct rtsx_chip *chip, int select);
+int sd_pull_ctl_enable(struct rtsx_chip *chip);
+int reset_sd_card(struct rtsx_chip *chip);
+int sd_switch_clock(struct rtsx_chip *chip);
+void sd_stop_seq_mode(struct rtsx_chip *chip);
+int sd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+       u32 start_sector, u16 sector_cnt);
+void sd_cleanup_work(struct rtsx_chip *chip);
+int sd_power_off_card3v3(struct rtsx_chip *chip);
+int release_sd_card(struct rtsx_chip *chip);
+#ifdef SUPPORT_CPRM
+int soft_reset_sd_card(struct rtsx_chip *chip);
+int ext_sd_send_cmd_get_rsp(struct rtsx_chip *chip, u8 cmd_idx,
+               u32 arg, u8 rsp_type, u8 *rsp, int rsp_len, int special_check);
+int ext_sd_get_rsp(struct rtsx_chip *chip, int len, u8 *rsp, u8 rsp_type);
+
+int sd_pass_thru_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int sd_execute_no_data(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int sd_execute_read_data(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int sd_execute_write_data(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int sd_get_cmd_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int sd_hw_rst(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+#endif
+
+#endif  /* __REALTEK_RTSX_SD_H */
diff --git a/drivers/staging/rts5208/spi.c b/drivers/staging/rts5208/spi.c
new file mode 100644 (file)
index 0000000..312b9f9
--- /dev/null
@@ -0,0 +1,877 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+
+#include "rtsx.h"
+#include "rtsx_transport.h"
+#include "rtsx_scsi.h"
+#include "rtsx_card.h"
+#include "spi.h"
+
+static inline void spi_set_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+       struct spi_info *spi = &(chip->spi);
+
+       spi->err_code = err_code;
+}
+
+static int spi_init(struct rtsx_chip *chip)
+{
+       RTSX_WRITE_REG(chip, SPI_CONTROL, 0xFF,
+               CS_POLARITY_LOW | DTO_MSB_FIRST | SPI_MASTER | SPI_MODE0 |
+               SPI_AUTO);
+       RTSX_WRITE_REG(chip, SPI_TCTL, EDO_TIMING_MASK, SAMPLE_DELAY_HALF);
+
+       return STATUS_SUCCESS;
+}
+
+static int spi_set_init_para(struct rtsx_chip *chip)
+{
+       struct spi_info *spi = &(chip->spi);
+       int retval;
+
+       RTSX_WRITE_REG(chip, SPI_CLK_DIVIDER1, 0xFF, (u8)(spi->clk_div >> 8));
+       RTSX_WRITE_REG(chip, SPI_CLK_DIVIDER0, 0xFF, (u8)(spi->clk_div));
+
+       retval = switch_clock(chip, spi->spi_clock);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = select_card(chip, SPI_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_WRITE_REG(chip, CARD_CLK_EN, SPI_CLK_EN, SPI_CLK_EN);
+       RTSX_WRITE_REG(chip, CARD_OE, SPI_OUTPUT_EN, SPI_OUTPUT_EN);
+
+       wait_timeout(10);
+
+       retval = spi_init(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int sf_polling_status(struct rtsx_chip *chip, int msec)
+{
+       int retval;
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, SPI_RDSR);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+               SPI_TRANSFER0_START | SPI_POLLING_MODE0);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+               SPI_TRANSFER0_END);
+
+       retval = rtsx_send_cmd(chip, 0, msec);
+       if (retval < 0) {
+               rtsx_clear_spi_error(chip);
+               spi_set_err_code(chip, SPI_BUSY_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sf_enable_write(struct rtsx_chip *chip, u8 ins)
+{
+       struct spi_info *spi = &(chip->spi);
+       int retval;
+
+       if (!spi->write_en)
+               return STATUS_SUCCESS;
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+               SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+               SPI_TRANSFER0_START | SPI_C_MODE0);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+               SPI_TRANSFER0_END);
+
+       retval = rtsx_send_cmd(chip, 0, 100);
+       if (retval < 0) {
+               rtsx_clear_spi_error(chip);
+               spi_set_err_code(chip, SPI_HW_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int sf_disable_write(struct rtsx_chip *chip, u8 ins)
+{
+       struct spi_info *spi = &(chip->spi);
+       int retval;
+
+       if (!spi->write_en)
+               return STATUS_SUCCESS;
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+               SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+               SPI_TRANSFER0_START | SPI_C_MODE0);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+               SPI_TRANSFER0_END);
+
+       retval = rtsx_send_cmd(chip, 0, 100);
+       if (retval < 0) {
+               rtsx_clear_spi_error(chip);
+               spi_set_err_code(chip, SPI_HW_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static void sf_program(struct rtsx_chip *chip, u8 ins, u8 addr_mode, u32 addr,
+               u16 len)
+{
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+               SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, (u8)len);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, (u8)(len >> 8));
+       if (addr_mode) {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF,
+                       (u8)(addr >> 8));
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF,
+                       (u8)(addr >> 16));
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+                       SPI_TRANSFER0_START | SPI_CADO_MODE0);
+       } else {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+                       SPI_TRANSFER0_START | SPI_CDO_MODE0);
+       }
+       rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+               SPI_TRANSFER0_END);
+}
+
+static int sf_erase(struct rtsx_chip *chip, u8 ins, u8 addr_mode, u32 addr)
+{
+       int retval;
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+               SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+       if (addr_mode) {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF,
+                       (u8)(addr >> 8));
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF,
+                       (u8)(addr >> 16));
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+                       SPI_TRANSFER0_START | SPI_CA_MODE0);
+       } else {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+                       SPI_TRANSFER0_START | SPI_C_MODE0);
+       }
+       rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+               SPI_TRANSFER0_END);
+
+       retval = rtsx_send_cmd(chip, 0, 100);
+       if (retval < 0) {
+               rtsx_clear_spi_error(chip);
+               spi_set_err_code(chip, SPI_HW_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int spi_init_eeprom(struct rtsx_chip *chip)
+{
+       int retval;
+       int clk;
+
+       if (chip->asic_code)
+               clk = 30;
+       else
+               clk = CLK_30;
+
+       RTSX_WRITE_REG(chip, SPI_CLK_DIVIDER1, 0xFF, 0x00);
+       RTSX_WRITE_REG(chip, SPI_CLK_DIVIDER0, 0xFF, 0x27);
+
+       retval = switch_clock(chip, clk);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = select_card(chip, SPI_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_WRITE_REG(chip, CARD_CLK_EN, SPI_CLK_EN, SPI_CLK_EN);
+       RTSX_WRITE_REG(chip, CARD_OE, SPI_OUTPUT_EN, SPI_OUTPUT_EN);
+
+       wait_timeout(10);
+
+       RTSX_WRITE_REG(chip, SPI_CONTROL, 0xFF,
+               CS_POLARITY_HIGH | SPI_EEPROM_AUTO);
+       RTSX_WRITE_REG(chip, SPI_TCTL, EDO_TIMING_MASK, SAMPLE_DELAY_HALF);
+
+       return STATUS_SUCCESS;
+}
+
+static int spi_eeprom_program_enable(struct rtsx_chip *chip)
+{
+       int retval;
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x86);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x13);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+               SPI_TRANSFER0_START | SPI_CA_MODE0);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+               SPI_TRANSFER0_END);
+
+       retval = rtsx_send_cmd(chip, 0, 100);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+int spi_erase_eeprom_chip(struct rtsx_chip *chip)
+{
+       int retval;
+
+       retval = spi_init_eeprom(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = spi_eeprom_program_enable(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x12);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x84);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+               SPI_TRANSFER0_START | SPI_CA_MODE0);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+               SPI_TRANSFER0_END);
+
+       retval = rtsx_send_cmd(chip, 0, 100);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_WRITE_REG(chip, CARD_GPIO_DIR, 0x01, 0x01);
+
+       return STATUS_SUCCESS;
+}
+
+int spi_erase_eeprom_byte(struct rtsx_chip *chip, u16 addr)
+{
+       int retval;
+
+       retval = spi_init_eeprom(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = spi_eeprom_program_enable(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x07);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, (u8)(addr >> 8));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x46);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+               SPI_TRANSFER0_START | SPI_CA_MODE0);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+               SPI_TRANSFER0_END);
+
+       retval = rtsx_send_cmd(chip, 0, 100);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_WRITE_REG(chip, CARD_GPIO_DIR, 0x01, 0x01);
+
+       return STATUS_SUCCESS;
+}
+
+
+int spi_read_eeprom(struct rtsx_chip *chip, u16 addr, u8 *val)
+{
+       int retval;
+       u8 data;
+
+       retval = spi_init_eeprom(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x06);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, (u8)(addr >> 8));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x46);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, 1);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+               SPI_TRANSFER0_START | SPI_CADI_MODE0);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+               SPI_TRANSFER0_END);
+
+       retval = rtsx_send_cmd(chip, 0, 100);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       wait_timeout(5);
+       RTSX_READ_REG(chip, SPI_DATA, &data);
+
+       if (val)
+               *val = data;
+
+       RTSX_WRITE_REG(chip, CARD_GPIO_DIR, 0x01, 0x01);
+
+       return STATUS_SUCCESS;
+}
+
+int spi_write_eeprom(struct rtsx_chip *chip, u16 addr, u8 val)
+{
+       int retval;
+
+       retval = spi_init_eeprom(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = spi_eeprom_program_enable(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x05);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, val);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, (u8)addr);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, (u8)(addr >> 8));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x4E);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+               SPI_TRANSFER0_START | SPI_CA_MODE0);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+               SPI_TRANSFER0_END);
+
+       retval = rtsx_send_cmd(chip, 0, 100);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_WRITE_REG(chip, CARD_GPIO_DIR, 0x01, 0x01);
+
+       return STATUS_SUCCESS;
+}
+
+
+int spi_get_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct spi_info *spi = &(chip->spi);
+
+       RTSX_DEBUGP("spi_get_status: err_code = 0x%x\n", spi->err_code);
+       rtsx_stor_set_xfer_buf(&(spi->err_code),
+                       min_t(int, scsi_bufflen(srb), 1), srb);
+       scsi_set_resid(srb, scsi_bufflen(srb) - 1);
+
+       return STATUS_SUCCESS;
+}
+
+int spi_set_parameter(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       struct spi_info *spi = &(chip->spi);
+
+       spi_set_err_code(chip, SPI_NO_ERR);
+
+       if (chip->asic_code)
+               spi->spi_clock = ((u16)(srb->cmnd[8]) << 8) | srb->cmnd[9];
+       else
+               spi->spi_clock = srb->cmnd[3];
+
+       spi->clk_div = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
+       spi->write_en = srb->cmnd[6];
+
+       RTSX_DEBUGP("spi_set_parameter: spi_clock = %d, clk_div = %d, write_en = %d\n",
+                    spi->spi_clock, spi->clk_div, spi->write_en);
+
+       return STATUS_SUCCESS;
+}
+
+int spi_read_flash_id(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval;
+       u16 len;
+       u8 *buf;
+
+       spi_set_err_code(chip, SPI_NO_ERR);
+
+       len = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
+       if (len > 512) {
+               spi_set_err_code(chip, SPI_INVALID_COMMAND);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = spi_set_init_para(chip);
+       if (retval != STATUS_SUCCESS) {
+               spi_set_err_code(chip, SPI_HW_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01,
+               PINGPONG_BUFFER);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, srb->cmnd[3]);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, srb->cmnd[4]);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, srb->cmnd[5]);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, srb->cmnd[6]);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+               SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, srb->cmnd[7]);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, srb->cmnd[8]);
+
+       if (len == 0) {
+               if (srb->cmnd[9]) {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0,
+                                     0xFF, SPI_TRANSFER0_START | SPI_CA_MODE0);
+               } else {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0,
+                                     0xFF, SPI_TRANSFER0_START | SPI_C_MODE0);
+               }
+       } else {
+               if (srb->cmnd[9]) {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+                               SPI_TRANSFER0_START | SPI_CADI_MODE0);
+               } else {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+                               SPI_TRANSFER0_START | SPI_CDI_MODE0);
+               }
+       }
+
+       rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+               SPI_TRANSFER0_END);
+
+       retval = rtsx_send_cmd(chip, 0, 100);
+       if (retval < 0) {
+               rtsx_clear_spi_error(chip);
+               spi_set_err_code(chip, SPI_HW_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (len) {
+               buf = kmalloc(len, GFP_KERNEL);
+               if (!buf)
+                       TRACE_RET(chip, STATUS_ERROR);
+
+               retval = rtsx_read_ppbuf(chip, buf, len);
+               if (retval != STATUS_SUCCESS) {
+                       spi_set_err_code(chip, SPI_READ_ERR);
+                       kfree(buf);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
+               scsi_set_resid(srb, 0);
+
+               kfree(buf);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int spi_read_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval;
+       unsigned int index = 0, offset = 0;
+       u8 ins, slow_read;
+       u32 addr;
+       u16 len;
+       u8 *buf;
+
+       spi_set_err_code(chip, SPI_NO_ERR);
+
+       ins = srb->cmnd[3];
+       addr = ((u32)(srb->cmnd[4]) << 16) | ((u32)(srb->cmnd[5])
+                                       << 8) | srb->cmnd[6];
+       len = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
+       slow_read = srb->cmnd[9];
+
+       retval = spi_set_init_para(chip);
+       if (retval != STATUS_SUCCESS) {
+               spi_set_err_code(chip, SPI_HW_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       buf = kmalloc(SF_PAGE_LEN, GFP_KERNEL);
+       if (buf == NULL)
+               TRACE_RET(chip, STATUS_ERROR);
+
+       while (len) {
+               u16 pagelen = SF_PAGE_LEN - (u8)addr;
+
+               if (pagelen > len)
+                       pagelen = len;
+
+               rtsx_init_cmd(chip);
+
+               trans_dma_enable(DMA_FROM_DEVICE, chip, 256, DMA_256);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
+
+               if (slow_read) {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF,
+                               (u8)addr);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF,
+                               (u8)(addr >> 8));
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF,
+                               (u8)(addr >> 16));
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+                               SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+               } else {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF,
+                               (u8)addr);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF,
+                               (u8)(addr >> 8));
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR3, 0xFF,
+                               (u8)(addr >> 16));
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+                               SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_32);
+               }
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF,
+                       (u8)(pagelen >> 8));
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF,
+                       (u8)pagelen);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+                       SPI_TRANSFER0_START | SPI_CADI_MODE0);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0,
+                       SPI_TRANSFER0_END, SPI_TRANSFER0_END);
+
+               rtsx_send_cmd_no_wait(chip);
+
+               retval = rtsx_transfer_data(chip, 0, buf, pagelen, 0,
+                                       DMA_FROM_DEVICE, 10000);
+               if (retval < 0) {
+                       kfree(buf);
+                       rtsx_clear_spi_error(chip);
+                       spi_set_err_code(chip, SPI_HW_ERR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               rtsx_stor_access_xfer_buf(buf, pagelen, srb, &index, &offset,
+                                       TO_XFER_BUF);
+
+               addr += pagelen;
+               len -= pagelen;
+       }
+
+       scsi_set_resid(srb, 0);
+       kfree(buf);
+
+       return STATUS_SUCCESS;
+}
+
+int spi_write_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval;
+       u8 ins, program_mode;
+       u32 addr;
+       u16 len;
+       u8 *buf;
+       unsigned int index = 0, offset = 0;
+
+       spi_set_err_code(chip, SPI_NO_ERR);
+
+       ins = srb->cmnd[3];
+       addr = ((u32)(srb->cmnd[4]) << 16) | ((u32)(srb->cmnd[5])
+                                       << 8) | srb->cmnd[6];
+       len = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
+       program_mode = srb->cmnd[9];
+
+       retval = spi_set_init_para(chip);
+       if (retval != STATUS_SUCCESS) {
+               spi_set_err_code(chip, SPI_HW_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (program_mode == BYTE_PROGRAM) {
+               buf = kmalloc(4, GFP_KERNEL);
+               if (!buf)
+                       TRACE_RET(chip, STATUS_ERROR);
+
+               while (len) {
+                       retval = sf_enable_write(chip, SPI_WREN);
+                       if (retval != STATUS_SUCCESS) {
+                               kfree(buf);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       rtsx_stor_access_xfer_buf(buf, 1, srb, &index, &offset,
+                                               FROM_XFER_BUF);
+
+                       rtsx_init_cmd(chip);
+
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                               0x01, PINGPONG_BUFFER);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2, 0xFF,
+                               buf[0]);
+                       sf_program(chip, ins, 1, addr, 1);
+
+                       retval = rtsx_send_cmd(chip, 0, 100);
+                       if (retval < 0) {
+                               kfree(buf);
+                               rtsx_clear_spi_error(chip);
+                               spi_set_err_code(chip, SPI_HW_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       retval = sf_polling_status(chip, 100);
+                       if (retval != STATUS_SUCCESS) {
+                               kfree(buf);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       addr++;
+                       len--;
+               }
+
+               kfree(buf);
+
+       } else if (program_mode == AAI_PROGRAM) {
+               int first_byte = 1;
+
+               retval = sf_enable_write(chip, SPI_WREN);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               buf = kmalloc(4, GFP_KERNEL);
+               if (!buf)
+                       TRACE_RET(chip, STATUS_ERROR);
+
+               while (len) {
+                       rtsx_stor_access_xfer_buf(buf, 1, srb, &index, &offset,
+                                               FROM_XFER_BUF);
+
+                       rtsx_init_cmd(chip);
+
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                               0x01, PINGPONG_BUFFER);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2, 0xFF,
+                               buf[0]);
+                       if (first_byte) {
+                               sf_program(chip, ins, 1, addr, 1);
+                               first_byte = 0;
+                       } else {
+                               sf_program(chip, ins, 0, 0, 1);
+                       }
+
+                       retval = rtsx_send_cmd(chip, 0, 100);
+                       if (retval < 0) {
+                               kfree(buf);
+                               rtsx_clear_spi_error(chip);
+                               spi_set_err_code(chip, SPI_HW_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       retval = sf_polling_status(chip, 100);
+                       if (retval != STATUS_SUCCESS) {
+                               kfree(buf);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       len--;
+               }
+
+               kfree(buf);
+
+               retval = sf_disable_write(chip, SPI_WRDI);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = sf_polling_status(chip, 100);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       } else if (program_mode == PAGE_PROGRAM) {
+               buf = kmalloc(SF_PAGE_LEN, GFP_KERNEL);
+               if (!buf)
+                       TRACE_RET(chip, STATUS_NOMEM);
+
+               while (len) {
+                       u16 pagelen = SF_PAGE_LEN - (u8)addr;
+
+                       if (pagelen > len)
+                               pagelen = len;
+
+                       retval = sf_enable_write(chip, SPI_WREN);
+                       if (retval != STATUS_SUCCESS) {
+                               kfree(buf);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       rtsx_init_cmd(chip);
+
+                       trans_dma_enable(DMA_TO_DEVICE, chip, 256, DMA_256);
+                       sf_program(chip, ins, 1, addr, pagelen);
+
+                       rtsx_send_cmd_no_wait(chip);
+
+                       rtsx_stor_access_xfer_buf(buf, pagelen, srb, &index,
+                                               &offset, FROM_XFER_BUF);
+
+                       retval = rtsx_transfer_data(chip, 0, buf, pagelen, 0,
+                                               DMA_TO_DEVICE, 100);
+                       if (retval < 0) {
+                               kfree(buf);
+                               rtsx_clear_spi_error(chip);
+                               spi_set_err_code(chip, SPI_HW_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       retval = sf_polling_status(chip, 100);
+                       if (retval != STATUS_SUCCESS) {
+                               kfree(buf);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       addr += pagelen;
+                       len -= pagelen;
+               }
+
+               kfree(buf);
+       } else {
+               spi_set_err_code(chip, SPI_INVALID_COMMAND);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int spi_erase_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval;
+       u8 ins, erase_mode;
+       u32 addr;
+
+       spi_set_err_code(chip, SPI_NO_ERR);
+
+       ins = srb->cmnd[3];
+       addr = ((u32)(srb->cmnd[4]) << 16) | ((u32)(srb->cmnd[5])
+                                       << 8) | srb->cmnd[6];
+       erase_mode = srb->cmnd[9];
+
+       retval = spi_set_init_para(chip);
+       if (retval != STATUS_SUCCESS) {
+               spi_set_err_code(chip, SPI_HW_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       if (erase_mode == PAGE_ERASE) {
+               retval = sf_enable_write(chip, SPI_WREN);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = sf_erase(chip, ins, 1, addr);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       } else if (erase_mode == CHIP_ERASE) {
+               retval = sf_enable_write(chip, SPI_WREN);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = sf_erase(chip, ins, 0, 0);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       } else {
+               spi_set_err_code(chip, SPI_INVALID_COMMAND);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int spi_write_flash_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+       int retval;
+       u8 ins, status, ewsr;
+
+       ins = srb->cmnd[3];
+       status = srb->cmnd[4];
+       ewsr = srb->cmnd[5];
+
+       retval = spi_set_init_para(chip);
+       if (retval != STATUS_SUCCESS) {
+               spi_set_err_code(chip, SPI_HW_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = sf_enable_write(chip, ewsr);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01,
+               PINGPONG_BUFFER);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+               SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, 0);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, 1);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2, 0xFF, status);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+               SPI_TRANSFER0_START | SPI_CDO_MODE0);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+               SPI_TRANSFER0_END);
+
+       retval = rtsx_send_cmd(chip, 0, 100);
+       if (retval != STATUS_SUCCESS) {
+               rtsx_clear_spi_error(chip);
+               spi_set_err_code(chip, SPI_HW_ERR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
diff --git a/drivers/staging/rts5208/spi.h b/drivers/staging/rts5208/spi.h
new file mode 100644 (file)
index 0000000..fc824b5
--- /dev/null
@@ -0,0 +1,65 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_SPI_H
+#define __REALTEK_RTSX_SPI_H
+
+/* SPI operation error */
+#define SPI_NO_ERR             0x00
+#define SPI_HW_ERR             0x01
+#define SPI_INVALID_COMMAND    0x02
+#define SPI_READ_ERR           0x03
+#define SPI_WRITE_ERR          0x04
+#define SPI_ERASE_ERR          0x05
+#define SPI_BUSY_ERR           0x06
+
+/* Serial flash instruction */
+#define SPI_READ               0x03
+#define SPI_FAST_READ          0x0B
+#define SPI_WREN               0x06
+#define SPI_WRDI               0x04
+#define SPI_RDSR               0x05
+
+#define SF_PAGE_LEN            256
+
+#define BYTE_PROGRAM           0
+#define AAI_PROGRAM            1
+#define PAGE_PROGRAM           2
+
+#define PAGE_ERASE             0
+#define CHIP_ERASE             1
+
+int spi_erase_eeprom_chip(struct rtsx_chip *chip);
+int spi_erase_eeprom_byte(struct rtsx_chip *chip, u16 addr);
+int spi_read_eeprom(struct rtsx_chip *chip, u16 addr, u8 *val);
+int spi_write_eeprom(struct rtsx_chip *chip, u16 addr, u8 val);
+int spi_get_status(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int spi_set_parameter(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int spi_read_flash_id(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int spi_read_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int spi_write_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int spi_erase_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int spi_write_flash_status(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+
+
+#endif  /* __REALTEK_RTSX_SPI_H */
diff --git a/drivers/staging/rts5208/trace.h b/drivers/staging/rts5208/trace.h
new file mode 100644 (file)
index 0000000..0f177fb
--- /dev/null
@@ -0,0 +1,93 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_TRACE_H
+#define __REALTEK_RTSX_TRACE_H
+
+#define _MSG_TRACE
+
+#ifdef _MSG_TRACE
+static inline char *filename(char *path)
+{
+       char *ptr;
+
+       if (path == NULL)
+               return NULL;
+
+       ptr = path;
+
+       while (*ptr != '\0') {
+               if ((*ptr == '\\') || (*ptr == '/'))
+                       path = ptr + 1;
+
+               ptr++;
+       }
+
+       return path;
+}
+
+#define TRACE_RET(chip, ret)                                           \
+       do {                                                            \
+               char *_file = filename(__FILE__);                       \
+               RTSX_DEBUGP("[%s][%s]:[%d]\n", _file, __func__, __LINE__); \
+               (chip)->trace_msg[(chip)->msg_idx].line = (u16)(__LINE__); \
+               strncpy((chip)->trace_msg[(chip)->msg_idx].func, __func__, MSG_FUNC_LEN-1); \
+               strncpy((chip)->trace_msg[(chip)->msg_idx].file, _file, MSG_FILE_LEN-1); \
+               get_current_time((chip)->trace_msg[(chip)->msg_idx].timeval_buf, TIME_VAL_LEN); \
+               (chip)->trace_msg[(chip)->msg_idx].valid = 1;           \
+               (chip)->msg_idx++;                                      \
+               if ((chip)->msg_idx >= TRACE_ITEM_CNT) {                \
+                       (chip)->msg_idx = 0;                            \
+               }                                                       \
+               return ret;                                             \
+       } while (0)
+
+#define TRACE_GOTO(chip, label)                                                \
+       do {                                                            \
+               char *_file = filename(__FILE__);                       \
+               RTSX_DEBUGP("[%s][%s]:[%d]\n", _file, __func__, __LINE__); \
+               (chip)->trace_msg[(chip)->msg_idx].line = (u16)(__LINE__); \
+               strncpy((chip)->trace_msg[(chip)->msg_idx].func, __func__, MSG_FUNC_LEN-1); \
+               strncpy((chip)->trace_msg[(chip)->msg_idx].file, _file, MSG_FILE_LEN-1); \
+               get_current_time((chip)->trace_msg[(chip)->msg_idx].timeval_buf, TIME_VAL_LEN); \
+               (chip)->trace_msg[(chip)->msg_idx].valid = 1;           \
+               (chip)->msg_idx++;                                      \
+               if ((chip)->msg_idx >= TRACE_ITEM_CNT) {                \
+                       (chip)->msg_idx = 0;                            \
+               }                                                       \
+               goto label;                                             \
+       } while (0)
+#else
+#define TRACE_RET(chip, ret)   return ret
+#define TRACE_GOTO(chip, label)        goto label
+#endif
+
+#ifdef CONFIG_RTS5208_DEBUG
+#define RTSX_DUMP(buf, buf_len)                                        \
+       print_hex_dump(KERN_DEBUG, RTSX_STOR, DUMP_PREFIX_NONE, \
+                               16, 1, (buf), (buf_len), false)
+#else
+#define RTSX_DUMP(buf, buf_len)
+#endif
+
+#endif  /* __REALTEK_RTSX_TRACE_H */
diff --git a/drivers/staging/rts5208/xd.c b/drivers/staging/rts5208/xd.c
new file mode 100644 (file)
index 0000000..6aef53d
--- /dev/null
@@ -0,0 +1,2088 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#include "rtsx.h"
+#include "rtsx_transport.h"
+#include "rtsx_scsi.h"
+#include "rtsx_card.h"
+#include "xd.h"
+
+static int xd_build_l2p_tbl(struct rtsx_chip *chip, int zone_no);
+static int xd_init_page(struct rtsx_chip *chip, u32 phy_blk, u16 logoff,
+                       u8 start_page, u8 end_page);
+
+static inline void xd_set_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+
+       xd_card->err_code = err_code;
+}
+
+static inline int xd_check_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+
+       return (xd_card->err_code == err_code);
+}
+
+static int xd_set_init_para(struct rtsx_chip *chip)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       int retval;
+
+       if (chip->asic_code)
+               xd_card->xd_clock = 47;
+       else
+               xd_card->xd_clock = CLK_50;
+
+       retval = switch_clock(chip, xd_card->xd_clock);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int xd_switch_clock(struct rtsx_chip *chip)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       int retval;
+
+       retval = select_card(chip, XD_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = switch_clock(chip, xd_card->xd_clock);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int xd_read_id(struct rtsx_chip *chip, u8 id_cmd, u8 *id_buf, u8 buf_len)
+{
+       int retval, i;
+       u8 *ptr;
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_DAT, 0xFF, id_cmd);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+               XD_TRANSFER_START | XD_READ_ID);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, XD_TRANSFER_END,
+               XD_TRANSFER_END);
+
+       for (i = 0; i < 4; i++)
+               rtsx_add_cmd(chip, READ_REG_CMD, (u16)(XD_ADDRESS1 + i), 0, 0);
+
+       retval = rtsx_send_cmd(chip, XD_CARD, 20);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ptr = rtsx_get_cmd_data(chip) + 1;
+       if (id_buf && buf_len) {
+               if (buf_len > 4)
+                       buf_len = 4;
+               memcpy(id_buf, ptr, buf_len);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static void xd_assign_phy_addr(struct rtsx_chip *chip, u32 addr, u8 mode)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+
+       switch (mode) {
+       case XD_RW_ADDR:
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS0, 0xFF, 0);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS1, 0xFF, (u8)addr);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS2,
+                       0xFF, (u8)(addr >> 8));
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS3,
+                       0xFF, (u8)(addr >> 16));
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, 0xFF,
+                       xd_card->addr_cycle | XD_CALC_ECC | XD_BA_NO_TRANSFORM);
+               break;
+
+       case XD_ERASE_ADDR:
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS0, 0xFF, (u8)addr);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS1,
+                       0xFF, (u8)(addr >> 8));
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS2,
+                       0xFF, (u8)(addr >> 16));
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, 0xFF,
+                       (xd_card->addr_cycle - 1) | XD_CALC_ECC |
+                       XD_BA_NO_TRANSFORM);
+               break;
+
+       default:
+               break;
+       }
+}
+
+static int xd_read_redundant(struct rtsx_chip *chip, u32 page_addr,
+                       u8 *buf, int buf_len)
+{
+       int retval, i;
+
+       rtsx_init_cmd(chip);
+
+       xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER,
+               0xFF, XD_TRANSFER_START | XD_READ_REDUNDANT);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+               XD_TRANSFER_END, XD_TRANSFER_END);
+
+       for (i = 0; i < 6; i++)
+               rtsx_add_cmd(chip, READ_REG_CMD, (u16)(XD_PAGE_STATUS + i),
+                       0, 0);
+       for (i = 0; i < 4; i++)
+               rtsx_add_cmd(chip, READ_REG_CMD, (u16)(XD_RESERVED0 + i),
+                       0, 0);
+       rtsx_add_cmd(chip, READ_REG_CMD, XD_PARITY, 0, 0);
+
+       retval = rtsx_send_cmd(chip, XD_CARD, 500);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (buf && buf_len) {
+               u8 *ptr = rtsx_get_cmd_data(chip) + 1;
+
+               if (buf_len > 11)
+                       buf_len = 11;
+               memcpy(buf, ptr, buf_len);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int xd_read_data_from_ppb(struct rtsx_chip *chip, int offset,
+                               u8 *buf, int buf_len)
+{
+       int retval, i;
+
+       if (!buf || (buf_len < 0))
+               TRACE_RET(chip, STATUS_FAIL);
+
+       rtsx_init_cmd(chip);
+
+       for (i = 0; i < buf_len; i++)
+               rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + offset + i,
+                       0, 0);
+
+       retval = rtsx_send_cmd(chip, 0, 250);
+       if (retval < 0) {
+               rtsx_clear_xd_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       memcpy(buf, rtsx_get_cmd_data(chip), buf_len);
+
+       return STATUS_SUCCESS;
+}
+
+static int xd_read_cis(struct rtsx_chip *chip, u32 page_addr, u8 *buf,
+               int buf_len)
+{
+       int retval;
+       u8 reg;
+
+       if (!buf || (buf_len < 10))
+               TRACE_RET(chip, STATUS_FAIL);
+
+       rtsx_init_cmd(chip);
+
+       xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+               0x01, PINGPONG_BUFFER);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, 1);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CHK_DATA_STATUS,
+               XD_AUTO_CHK_DATA_STATUS, XD_AUTO_CHK_DATA_STATUS);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+               XD_TRANSFER_START | XD_READ_PAGES);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, XD_TRANSFER_END,
+               XD_TRANSFER_END);
+
+       retval = rtsx_send_cmd(chip, XD_CARD, 250);
+       if (retval == -ETIMEDOUT) {
+               rtsx_clear_xd_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       RTSX_READ_REG(chip, XD_PAGE_STATUS, &reg);
+       if (reg != XD_GPG) {
+               rtsx_clear_xd_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       RTSX_READ_REG(chip, XD_CTL, &reg);
+       if (!(reg & XD_ECC1_ERROR) || !(reg & XD_ECC1_UNCORRECTABLE)) {
+               retval = xd_read_data_from_ppb(chip, 0, buf, buf_len);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+               if (reg & XD_ECC1_ERROR) {
+                       u8 ecc_bit, ecc_byte;
+
+                       RTSX_READ_REG(chip, XD_ECC_BIT1, &ecc_bit);
+                       RTSX_READ_REG(chip, XD_ECC_BYTE1, &ecc_byte);
+
+                       RTSX_DEBUGP("ECC_BIT1 = 0x%x, ECC_BYTE1 = 0x%x\n",
+                               ecc_bit, ecc_byte);
+                       if (ecc_byte < buf_len) {
+                               RTSX_DEBUGP("Before correct: 0x%x\n",
+                                       buf[ecc_byte]);
+                               buf[ecc_byte] ^= (1 << ecc_bit);
+                               RTSX_DEBUGP("After correct: 0x%x\n",
+                                       buf[ecc_byte]);
+                       }
+               }
+       } else if (!(reg & XD_ECC2_ERROR) || !(reg & XD_ECC2_UNCORRECTABLE)) {
+               rtsx_clear_xd_error(chip);
+
+               retval = xd_read_data_from_ppb(chip, 256, buf, buf_len);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+               if (reg & XD_ECC2_ERROR) {
+                       u8 ecc_bit, ecc_byte;
+
+                       RTSX_READ_REG(chip, XD_ECC_BIT2, &ecc_bit);
+                       RTSX_READ_REG(chip, XD_ECC_BYTE2, &ecc_byte);
+
+                       RTSX_DEBUGP("ECC_BIT2 = 0x%x, ECC_BYTE2 = 0x%x\n",
+                               ecc_bit, ecc_byte);
+                       if (ecc_byte < buf_len) {
+                               RTSX_DEBUGP("Before correct: 0x%x\n",
+                                       buf[ecc_byte]);
+                               buf[ecc_byte] ^= (1 << ecc_bit);
+                               RTSX_DEBUGP("After correct: 0x%x\n",
+                                       buf[ecc_byte]);
+                       }
+               }
+       } else {
+               rtsx_clear_xd_error(chip);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static void xd_fill_pull_ctl_disable(struct rtsx_chip *chip)
+{
+       if (CHECK_PID(chip, 0x5208)) {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF,
+                       XD_D3_PD | XD_D2_PD | XD_D1_PD | XD_D0_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF,
+                       XD_D7_PD | XD_D6_PD | XD_D5_PD | XD_D4_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF,
+                       XD_WP_PD | XD_CE_PD | XD_CLE_PD | XD_CD_PU);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF,
+                       XD_RDY_PD | XD_WE_PD | XD_RE_PD | XD_ALE_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF,
+                       MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF,
+                       MS_D5_PD | MS_D4_PD);
+       } else if (CHECK_PID(chip, 0x5288)) {
+               if (CHECK_BARO_PKG(chip, QFN)) {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1,
+                               0xFF, 0x55);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2,
+                               0xFF, 0x55);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3,
+                               0xFF, 0x4B);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4,
+                               0xFF, 0x69);
+               }
+       }
+}
+
+static void xd_fill_pull_ctl_stage1_barossa(struct rtsx_chip *chip)
+{
+       if (CHECK_BARO_PKG(chip, QFN)) {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x4B);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
+       }
+}
+
+static void xd_fill_pull_ctl_enable(struct rtsx_chip *chip)
+{
+       if (CHECK_PID(chip, 0x5208)) {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF,
+                       XD_D3_PD | XD_D2_PD | XD_D1_PD | XD_D0_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF,
+                       XD_D7_PD | XD_D6_PD | XD_D5_PD | XD_D4_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF,
+                       XD_WP_PD | XD_CE_PU | XD_CLE_PD | XD_CD_PU);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF,
+                       XD_RDY_PU | XD_WE_PU | XD_RE_PU | XD_ALE_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF,
+                       MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF,
+                       MS_D5_PD | MS_D4_PD);
+       } else if (CHECK_PID(chip, 0x5288)) {
+               if (CHECK_BARO_PKG(chip, QFN)) {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1,
+                               0xFF, 0x55);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2,
+                               0xFF, 0x55);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3,
+                               0xFF, 0x53);
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4,
+                               0xFF, 0xA9);
+               }
+       }
+}
+
+static int xd_pull_ctl_disable(struct rtsx_chip *chip)
+{
+       if (CHECK_PID(chip, 0x5208)) {
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL1, 0xFF,
+                       XD_D3_PD | XD_D2_PD | XD_D1_PD | XD_D0_PD);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL2, 0xFF,
+                       XD_D7_PD | XD_D6_PD | XD_D5_PD | XD_D4_PD);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL3, 0xFF,
+                       XD_WP_PD | XD_CE_PD | XD_CLE_PD | XD_CD_PU);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL4, 0xFF,
+                       XD_RDY_PD | XD_WE_PD | XD_RE_PD | XD_ALE_PD);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL5, 0xFF,
+                       MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD);
+               RTSX_WRITE_REG(chip, CARD_PULL_CTL6, 0xFF, MS_D5_PD | MS_D4_PD);
+       } else if (CHECK_PID(chip, 0x5288)) {
+               if (CHECK_BARO_PKG(chip, QFN)) {
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL1, 0xFF, 0x55);
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL2, 0xFF, 0x55);
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL3, 0xFF, 0x4B);
+                       RTSX_WRITE_REG(chip, CARD_PULL_CTL4, 0xFF, 0x69);
+               }
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int reset_xd(struct rtsx_chip *chip)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       int retval, i, j;
+       u8 *ptr, id_buf[4], redunt[11];
+
+       retval = select_card(chip, XD_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CHK_DATA_STATUS, 0xFF,
+               XD_PGSTS_NOT_FF);
+       if (chip->asic_code) {
+               if (!CHECK_PID(chip, 0x5288))
+                       xd_fill_pull_ctl_disable(chip);
+               else
+                       xd_fill_pull_ctl_stage1_barossa(chip);
+       } else {
+               rtsx_add_cmd(chip, WRITE_REG_CMD, FPGA_PULL_CTL, 0xFF,
+                       (FPGA_XD_PULL_CTL_EN1 & FPGA_XD_PULL_CTL_EN3) | 0x20);
+       }
+
+       if (!chip->ft2_fast_mode)
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_INIT,
+                       XD_NO_AUTO_PWR_OFF, 0);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_OE, XD_OUTPUT_EN, 0);
+
+       retval = rtsx_send_cmd(chip, XD_CARD, 100);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (!chip->ft2_fast_mode) {
+               retval = card_power_off(chip, XD_CARD);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               wait_timeout(250);
+
+               rtsx_init_cmd(chip);
+
+               if (chip->asic_code) {
+                       xd_fill_pull_ctl_enable(chip);
+               } else {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, FPGA_PULL_CTL, 0xFF,
+                               (FPGA_XD_PULL_CTL_EN1 & FPGA_XD_PULL_CTL_EN2) |
+                               0x20);
+               }
+
+               retval = rtsx_send_cmd(chip, XD_CARD, 100);
+               if (retval < 0)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               retval = card_power_on(chip, XD_CARD);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+#ifdef SUPPORT_OCP
+               wait_timeout(50);
+               if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
+                       RTSX_DEBUGP("Over current, OCPSTAT is 0x%x\n",
+                               chip->ocp_stat);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+#endif
+       }
+
+       rtsx_init_cmd(chip);
+
+       if (chip->ft2_fast_mode) {
+               if (chip->asic_code) {
+                       xd_fill_pull_ctl_enable(chip);
+               } else {
+                       rtsx_add_cmd(chip, WRITE_REG_CMD, FPGA_PULL_CTL, 0xFF,
+                               (FPGA_XD_PULL_CTL_EN1 & FPGA_XD_PULL_CTL_EN2) |
+                               0x20);
+               }
+       }
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_OE, XD_OUTPUT_EN, XD_OUTPUT_EN);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CTL, XD_CE_DISEN, XD_CE_DISEN);
+
+       retval = rtsx_send_cmd(chip, XD_CARD, 100);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if (!chip->ft2_fast_mode)
+               wait_timeout(200);
+
+       retval = xd_set_init_para(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       /* Read ID to check if the timing setting is right */
+       for (i = 0; i < 4; i++) {
+               rtsx_init_cmd(chip);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_DTCTL, 0xFF,
+                       XD_TIME_SETUP_STEP * 3 +
+                       XD_TIME_RW_STEP * (2 + i) + XD_TIME_RWN_STEP * i);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CATCTL, 0xFF,
+                       XD_TIME_SETUP_STEP * 3 + XD_TIME_RW_STEP * (4 + i) +
+                       XD_TIME_RWN_STEP * (3 + i));
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+                       XD_TRANSFER_START | XD_RESET);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+                       XD_TRANSFER_END, XD_TRANSFER_END);
+
+               rtsx_add_cmd(chip, READ_REG_CMD, XD_DAT, 0, 0);
+               rtsx_add_cmd(chip, READ_REG_CMD, XD_CTL, 0, 0);
+
+               retval = rtsx_send_cmd(chip, XD_CARD, 100);
+               if (retval < 0)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               ptr = rtsx_get_cmd_data(chip) + 1;
+
+               RTSX_DEBUGP("XD_DAT: 0x%x, XD_CTL: 0x%x\n", ptr[0], ptr[1]);
+
+               if (((ptr[0] & READY_FLAG) != READY_STATE) ||
+                       !(ptr[1] & XD_RDY))
+                       continue;
+
+               retval = xd_read_id(chip, READ_ID, id_buf, 4);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               RTSX_DEBUGP("READ_ID: 0x%x 0x%x 0x%x 0x%x\n",
+                       id_buf[0], id_buf[1], id_buf[2], id_buf[3]);
+
+               xd_card->device_code = id_buf[1];
+
+               /* Check if the xD card is supported */
+               switch (xd_card->device_code) {
+               case XD_4M_X8_512_1:
+               case XD_4M_X8_512_2:
+                       xd_card->block_shift = 4;
+                       xd_card->page_off = 0x0F;
+                       xd_card->addr_cycle = 3;
+                       xd_card->zone_cnt = 1;
+                       xd_card->capacity = 8000;
+                       XD_SET_4MB(xd_card);
+                       break;
+               case XD_8M_X8_512:
+                       xd_card->block_shift = 4;
+                       xd_card->page_off = 0x0F;
+                       xd_card->addr_cycle = 3;
+                       xd_card->zone_cnt = 1;
+                       xd_card->capacity = 16000;
+                       break;
+               case XD_16M_X8_512:
+                       XD_PAGE_512(xd_card);
+                       xd_card->addr_cycle = 3;
+                       xd_card->zone_cnt = 1;
+                       xd_card->capacity = 32000;
+                       break;
+               case XD_32M_X8_512:
+                       XD_PAGE_512(xd_card);
+                       xd_card->addr_cycle = 3;
+                       xd_card->zone_cnt = 2;
+                       xd_card->capacity = 64000;
+                       break;
+               case XD_64M_X8_512:
+                       XD_PAGE_512(xd_card);
+                       xd_card->addr_cycle = 4;
+                       xd_card->zone_cnt = 4;
+                       xd_card->capacity = 128000;
+                       break;
+               case XD_128M_X8_512:
+                       XD_PAGE_512(xd_card);
+                       xd_card->addr_cycle = 4;
+                       xd_card->zone_cnt = 8;
+                       xd_card->capacity = 256000;
+                       break;
+               case XD_256M_X8_512:
+                       XD_PAGE_512(xd_card);
+                       xd_card->addr_cycle = 4;
+                       xd_card->zone_cnt = 16;
+                       xd_card->capacity = 512000;
+                       break;
+               case XD_512M_X8:
+                       XD_PAGE_512(xd_card);
+                       xd_card->addr_cycle = 4;
+                       xd_card->zone_cnt = 32;
+                       xd_card->capacity = 1024000;
+                       break;
+               case xD_1G_X8_512:
+                       XD_PAGE_512(xd_card);
+                       xd_card->addr_cycle = 4;
+                       xd_card->zone_cnt = 64;
+                       xd_card->capacity = 2048000;
+                       break;
+               case xD_2G_X8_512:
+                       XD_PAGE_512(xd_card);
+                       xd_card->addr_cycle = 4;
+                       xd_card->zone_cnt = 128;
+                       xd_card->capacity = 4096000;
+                       break;
+               default:
+                       continue;
+               }
+
+               /* Confirm timing setting */
+               for (j = 0; j < 10; j++) {
+                       retval = xd_read_id(chip, READ_ID, id_buf, 4);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       if (id_buf[1] != xd_card->device_code)
+                               break;
+               }
+
+               if (j == 10)
+                       break;
+       }
+
+       if (i == 4) {
+               xd_card->block_shift = 0;
+               xd_card->page_off = 0;
+               xd_card->addr_cycle = 0;
+               xd_card->capacity = 0;
+
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       retval = xd_read_id(chip, READ_xD_ID, id_buf, 4);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+       RTSX_DEBUGP("READ_xD_ID: 0x%x 0x%x 0x%x 0x%x\n",
+                       id_buf[0], id_buf[1], id_buf[2], id_buf[3]);
+       if (id_buf[2] != XD_ID_CODE)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       /* Search CIS block */
+       for (i = 0; i < 24; i++) {
+               u32 page_addr;
+
+               if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               page_addr = (u32)i << xd_card->block_shift;
+
+               for (j = 0; j < 3; j++) {
+                       retval = xd_read_redundant(chip, page_addr, redunt, 11);
+                       if (retval == STATUS_SUCCESS)
+                               break;
+               }
+               if (j == 3)
+                       continue;
+
+               if (redunt[BLOCK_STATUS] != XD_GBLK)
+                       continue;
+
+               j = 0;
+               if (redunt[PAGE_STATUS] != XD_GPG) {
+                       for (j = 1; j <= 8; j++) {
+                               retval = xd_read_redundant(chip, page_addr + j,
+                                                       redunt, 11);
+                               if (retval == STATUS_SUCCESS) {
+                                       if (redunt[PAGE_STATUS] == XD_GPG)
+                                               break;
+                               }
+                       }
+
+                       if (j == 9)
+                               break;
+               }
+
+               /* Check CIS data */
+               if ((redunt[BLOCK_STATUS] == XD_GBLK) &&
+                       (redunt[PARITY] & XD_BA1_ALL0)) {
+                       u8 buf[10];
+
+                       page_addr += j;
+
+                       retval = xd_read_cis(chip, page_addr, buf, 10);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+
+                       if ((buf[0] == 0x01) && (buf[1] == 0x03) &&
+                               (buf[2] == 0xD9)
+                                       && (buf[3] == 0x01) && (buf[4] == 0xFF)
+                                       && (buf[5] == 0x18) && (buf[6] == 0x02)
+                                       && (buf[7] == 0xDF) && (buf[8] == 0x01)
+                                       && (buf[9] == 0x20)) {
+                               xd_card->cis_block = (u16)i;
+                       }
+               }
+
+               break;
+       }
+
+       RTSX_DEBUGP("CIS block: 0x%x\n", xd_card->cis_block);
+       if (xd_card->cis_block == 0xFFFF)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       chip->capacity[chip->card2lun[XD_CARD]] = xd_card->capacity;
+
+       return STATUS_SUCCESS;
+}
+
+static int xd_check_data_blank(u8 *redunt)
+{
+       int i;
+
+       for (i = 0; i < 6; i++) {
+               if (redunt[PAGE_STATUS + i] != 0xFF)
+                       return 0;
+       }
+
+       if ((redunt[PARITY] & (XD_ECC1_ALL1 | XD_ECC2_ALL1))
+               != (XD_ECC1_ALL1 | XD_ECC2_ALL1))
+               return 0;
+
+
+       for (i = 0; i < 4; i++) {
+               if (redunt[RESERVED0 + i] != 0xFF)
+                       return 0;
+       }
+
+       return 1;
+}
+
+static u16 xd_load_log_block_addr(u8 *redunt)
+{
+       u16 addr = 0xFFFF;
+
+       if (redunt[PARITY] & XD_BA1_BA2_EQL)
+               addr = ((u16)redunt[BLOCK_ADDR1_H] << 8) |
+                       redunt[BLOCK_ADDR1_L];
+       else if (redunt[PARITY] & XD_BA1_VALID)
+               addr = ((u16)redunt[BLOCK_ADDR1_H] << 8) |
+                       redunt[BLOCK_ADDR1_L];
+       else if (redunt[PARITY] & XD_BA2_VALID)
+               addr = ((u16)redunt[BLOCK_ADDR2_H] << 8) |
+                       redunt[BLOCK_ADDR2_L];
+
+       return addr;
+}
+
+static int xd_init_l2p_tbl(struct rtsx_chip *chip)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       int size, i;
+
+       RTSX_DEBUGP("xd_init_l2p_tbl: zone_cnt = %d\n", xd_card->zone_cnt);
+
+       if (xd_card->zone_cnt < 1)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       size = xd_card->zone_cnt * sizeof(struct zone_entry);
+       RTSX_DEBUGP("Buffer size for l2p table is %d\n", size);
+
+       xd_card->zone = vmalloc(size);
+       if (!xd_card->zone)
+               TRACE_RET(chip, STATUS_ERROR);
+
+       for (i = 0; i < xd_card->zone_cnt; i++) {
+               xd_card->zone[i].build_flag = 0;
+               xd_card->zone[i].l2p_table = NULL;
+               xd_card->zone[i].free_table = NULL;
+               xd_card->zone[i].get_index = 0;
+               xd_card->zone[i].set_index = 0;
+               xd_card->zone[i].unused_blk_cnt = 0;
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static inline void free_zone(struct zone_entry *zone)
+{
+       RTSX_DEBUGP("free_zone\n");
+
+       if (!zone)
+               return;
+
+       zone->build_flag = 0;
+       zone->set_index = 0;
+       zone->get_index = 0;
+       zone->unused_blk_cnt = 0;
+       if (zone->l2p_table) {
+               vfree(zone->l2p_table);
+               zone->l2p_table = NULL;
+       }
+       if (zone->free_table) {
+               vfree(zone->free_table);
+               zone->free_table = NULL;
+       }
+}
+
+static void xd_set_unused_block(struct rtsx_chip *chip, u32 phy_blk)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       struct zone_entry *zone;
+       int zone_no;
+
+       zone_no = (int)phy_blk >> 10;
+       if (zone_no >= xd_card->zone_cnt) {
+               RTSX_DEBUGP("Set unused block to invalid zone (zone_no = %d, zone_cnt = %d)\n",
+                       zone_no, xd_card->zone_cnt);
+               return;
+       }
+       zone = &(xd_card->zone[zone_no]);
+
+       if (zone->free_table == NULL) {
+               if (xd_build_l2p_tbl(chip, zone_no) != STATUS_SUCCESS)
+                       return;
+       }
+
+       if ((zone->set_index >= XD_FREE_TABLE_CNT)
+                       || (zone->set_index < 0)) {
+               free_zone(zone);
+               RTSX_DEBUGP("Set unused block fail, invalid set_index\n");
+               return;
+       }
+
+       RTSX_DEBUGP("Set unused block to index %d\n", zone->set_index);
+
+       zone->free_table[zone->set_index++] = (u16) (phy_blk & 0x3ff);
+       if (zone->set_index >= XD_FREE_TABLE_CNT)
+               zone->set_index = 0;
+       zone->unused_blk_cnt++;
+}
+
+static u32 xd_get_unused_block(struct rtsx_chip *chip, int zone_no)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       struct zone_entry *zone;
+       u32 phy_blk;
+
+       if (zone_no >= xd_card->zone_cnt) {
+               RTSX_DEBUGP("Get unused block from invalid zone (zone_no = %d, zone_cnt = %d)\n",
+                       zone_no, xd_card->zone_cnt);
+               return BLK_NOT_FOUND;
+       }
+       zone = &(xd_card->zone[zone_no]);
+
+       if ((zone->unused_blk_cnt == 0) ||
+               (zone->set_index == zone->get_index)) {
+               free_zone(zone);
+               RTSX_DEBUGP("Get unused block fail, no unused block available\n");
+               return BLK_NOT_FOUND;
+       }
+       if ((zone->get_index >= XD_FREE_TABLE_CNT) || (zone->get_index < 0)) {
+               free_zone(zone);
+               RTSX_DEBUGP("Get unused block fail, invalid get_index\n");
+               return BLK_NOT_FOUND;
+       }
+
+       RTSX_DEBUGP("Get unused block from index %d\n", zone->get_index);
+
+       phy_blk = zone->free_table[zone->get_index];
+       zone->free_table[zone->get_index++] = 0xFFFF;
+       if (zone->get_index >= XD_FREE_TABLE_CNT)
+               zone->get_index = 0;
+       zone->unused_blk_cnt--;
+
+       phy_blk += ((u32)(zone_no) << 10);
+       return phy_blk;
+}
+
+static void xd_set_l2p_tbl(struct rtsx_chip *chip,
+                       int zone_no, u16 log_off, u16 phy_off)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       struct zone_entry *zone;
+
+       zone = &(xd_card->zone[zone_no]);
+       zone->l2p_table[log_off] = phy_off;
+}
+
+static u32 xd_get_l2p_tbl(struct rtsx_chip *chip, int zone_no, u16 log_off)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       struct zone_entry *zone;
+       int retval;
+
+       zone = &(xd_card->zone[zone_no]);
+       if (zone->l2p_table[log_off] == 0xFFFF) {
+               u32 phy_blk = 0;
+               int i;
+
+#ifdef XD_DELAY_WRITE
+               retval = xd_delay_write(chip);
+               if (retval != STATUS_SUCCESS) {
+                       RTSX_DEBUGP("In xd_get_l2p_tbl, delay write fail!\n");
+                       return BLK_NOT_FOUND;
+               }
+#endif
+
+               if (zone->unused_blk_cnt <= 0) {
+                       RTSX_DEBUGP("No unused block!\n");
+                       return BLK_NOT_FOUND;
+               }
+
+               for (i = 0; i < zone->unused_blk_cnt; i++) {
+                       phy_blk = xd_get_unused_block(chip, zone_no);
+                       if (phy_blk == BLK_NOT_FOUND) {
+                               RTSX_DEBUGP("No unused block available!\n");
+                               return BLK_NOT_FOUND;
+                       }
+
+                       retval = xd_init_page(chip, phy_blk, log_off,
+                                       0, xd_card->page_off + 1);
+                       if (retval == STATUS_SUCCESS)
+                               break;
+               }
+               if (i >= zone->unused_blk_cnt) {
+                       RTSX_DEBUGP("No good unused block available!\n");
+                       return BLK_NOT_FOUND;
+               }
+
+               xd_set_l2p_tbl(chip, zone_no, log_off, (u16)(phy_blk & 0x3FF));
+               return phy_blk;
+       }
+
+       return (u32)zone->l2p_table[log_off] + ((u32)(zone_no) << 10);
+}
+
+int reset_xd_card(struct rtsx_chip *chip)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       int retval;
+
+       memset(xd_card, 0, sizeof(struct xd_info));
+
+       xd_card->block_shift = 0;
+       xd_card->page_off = 0;
+       xd_card->addr_cycle = 0;
+       xd_card->capacity = 0;
+       xd_card->zone_cnt = 0;
+       xd_card->cis_block = 0xFFFF;
+       xd_card->delay_write.delay_write_flag = 0;
+
+       retval = enable_card_clock(chip, XD_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = reset_xd(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       retval = xd_init_l2p_tbl(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int xd_mark_bad_block(struct rtsx_chip *chip, u32 phy_blk)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       int retval;
+       u32 page_addr;
+       u8 reg = 0;
+
+       RTSX_DEBUGP("mark block 0x%x as bad block\n", phy_blk);
+
+       if (phy_blk == BLK_NOT_FOUND)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_STATUS, 0xFF, XD_GPG);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_STATUS, 0xFF, XD_LATER_BBLK);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_H, 0xFF, 0xFF);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_L, 0xFF, 0xFF);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR2_H, 0xFF, 0xFF);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR2_L, 0xFF, 0xFF);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_RESERVED0, 0xFF, 0xFF);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_RESERVED1, 0xFF, 0xFF);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_RESERVED2, 0xFF, 0xFF);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_RESERVED3, 0xFF, 0xFF);
+
+       page_addr = phy_blk << xd_card->block_shift;
+
+       xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF,
+               xd_card->page_off + 1);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+               XD_TRANSFER_START | XD_WRITE_REDUNDANT);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+               XD_TRANSFER_END, XD_TRANSFER_END);
+
+       retval = rtsx_send_cmd(chip, XD_CARD, 500);
+       if (retval < 0) {
+               rtsx_clear_xd_error(chip);
+               rtsx_read_register(chip, XD_DAT, &reg);
+               if (reg & PROGRAM_ERROR)
+                       xd_set_err_code(chip, XD_PRG_ERROR);
+               else
+                       xd_set_err_code(chip, XD_TO_ERROR);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int xd_init_page(struct rtsx_chip *chip, u32 phy_blk,
+                       u16 logoff, u8 start_page, u8 end_page)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       int retval;
+       u32 page_addr;
+       u8 reg = 0;
+
+       RTSX_DEBUGP("Init block 0x%x\n", phy_blk);
+
+       if (start_page > end_page)
+               TRACE_RET(chip, STATUS_FAIL);
+       if (phy_blk == BLK_NOT_FOUND)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_STATUS, 0xFF, 0xFF);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_STATUS, 0xFF, 0xFF);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_H,
+               0xFF, (u8)(logoff >> 8));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_L, 0xFF, (u8)logoff);
+
+       page_addr = (phy_blk << xd_card->block_shift) + start_page;
+
+       xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG,
+               XD_BA_TRANSFORM, XD_BA_TRANSFORM);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT,
+               0xFF, (end_page - start_page));
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER,
+               0xFF, XD_TRANSFER_START | XD_WRITE_REDUNDANT);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+               XD_TRANSFER_END, XD_TRANSFER_END);
+
+       retval = rtsx_send_cmd(chip, XD_CARD, 500);
+       if (retval < 0) {
+               rtsx_clear_xd_error(chip);
+               rtsx_read_register(chip, XD_DAT, &reg);
+               if (reg & PROGRAM_ERROR) {
+                       xd_mark_bad_block(chip, phy_blk);
+                       xd_set_err_code(chip, XD_PRG_ERROR);
+               } else {
+                       xd_set_err_code(chip, XD_TO_ERROR);
+               }
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int xd_copy_page(struct rtsx_chip *chip, u32 old_blk, u32 new_blk,
+                       u8 start_page, u8 end_page)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       u32 old_page, new_page;
+       u8 i, reg = 0;
+       int retval;
+
+       RTSX_DEBUGP("Copy page from block 0x%x to block 0x%x\n",
+               old_blk, new_blk);
+
+       if (start_page > end_page)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       if ((old_blk == BLK_NOT_FOUND) || (new_blk == BLK_NOT_FOUND))
+               TRACE_RET(chip, STATUS_FAIL);
+
+       old_page = (old_blk << xd_card->block_shift) + start_page;
+       new_page = (new_blk << xd_card->block_shift) + start_page;
+
+       XD_CLR_BAD_NEWBLK(xd_card);
+
+       RTSX_WRITE_REG(chip, CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER);
+
+       for (i = start_page; i < end_page; i++) {
+               if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+                       rtsx_clear_xd_error(chip);
+                       xd_set_err_code(chip, XD_NO_CARD);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               rtsx_init_cmd(chip);
+
+               xd_assign_phy_addr(chip, old_page, XD_RW_ADDR);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, 1);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CHK_DATA_STATUS,
+                       XD_AUTO_CHK_DATA_STATUS, 0);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+                       XD_TRANSFER_START | XD_READ_PAGES);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+                       XD_TRANSFER_END, XD_TRANSFER_END);
+
+               retval = rtsx_send_cmd(chip, XD_CARD, 500);
+               if (retval < 0) {
+                       rtsx_clear_xd_error(chip);
+                       reg = 0;
+                       rtsx_read_register(chip, XD_CTL, &reg);
+                       if (reg & (XD_ECC1_ERROR | XD_ECC2_ERROR)) {
+                               wait_timeout(100);
+
+                               if (detect_card_cd(chip,
+                                       XD_CARD) != STATUS_SUCCESS) {
+                                       xd_set_err_code(chip, XD_NO_CARD);
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               }
+
+                               if (((reg & (XD_ECC1_ERROR | XD_ECC1_UNCORRECTABLE)) ==
+                                               (XD_ECC1_ERROR | XD_ECC1_UNCORRECTABLE))
+                                       || ((reg & (XD_ECC2_ERROR | XD_ECC2_UNCORRECTABLE)) ==
+                                               (XD_ECC2_ERROR | XD_ECC2_UNCORRECTABLE))) {
+                                       rtsx_write_register(chip,
+                                                       XD_PAGE_STATUS, 0xFF,
+                                                       XD_BPG);
+                                       rtsx_write_register(chip,
+                                                       XD_BLOCK_STATUS, 0xFF,
+                                                       XD_GBLK);
+                                       XD_SET_BAD_OLDBLK(xd_card);
+                                       RTSX_DEBUGP("old block 0x%x ecc error\n", old_blk);
+                               }
+                       } else {
+                               xd_set_err_code(chip, XD_TO_ERROR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+
+               if (XD_CHK_BAD_OLDBLK(xd_card))
+                       rtsx_clear_xd_error(chip);
+
+               rtsx_init_cmd(chip);
+
+               xd_assign_phy_addr(chip, new_page, XD_RW_ADDR);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, 1);
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+                            XD_TRANSFER_START | XD_WRITE_PAGES);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+                       XD_TRANSFER_END, XD_TRANSFER_END);
+
+               retval = rtsx_send_cmd(chip, XD_CARD, 300);
+               if (retval < 0) {
+                       rtsx_clear_xd_error(chip);
+                       reg = 0;
+                       rtsx_read_register(chip, XD_DAT, &reg);
+                       if (reg & PROGRAM_ERROR) {
+                               xd_mark_bad_block(chip, new_blk);
+                               xd_set_err_code(chip, XD_PRG_ERROR);
+                               XD_SET_BAD_NEWBLK(xd_card);
+                       } else {
+                               xd_set_err_code(chip, XD_TO_ERROR);
+                       }
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               old_page++;
+               new_page++;
+       }
+
+       return STATUS_SUCCESS;
+}
+
+static int xd_reset_cmd(struct rtsx_chip *chip)
+{
+       int retval;
+       u8 *ptr;
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER,
+               0xFF, XD_TRANSFER_START | XD_RESET);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+               XD_TRANSFER_END, XD_TRANSFER_END);
+       rtsx_add_cmd(chip, READ_REG_CMD, XD_DAT, 0, 0);
+       rtsx_add_cmd(chip, READ_REG_CMD, XD_CTL, 0, 0);
+
+       retval = rtsx_send_cmd(chip, XD_CARD, 100);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       ptr = rtsx_get_cmd_data(chip) + 1;
+       if (((ptr[0] & READY_FLAG) == READY_STATE) && (ptr[1] & XD_RDY))
+               return STATUS_SUCCESS;
+
+       TRACE_RET(chip, STATUS_FAIL);
+}
+
+static int xd_erase_block(struct rtsx_chip *chip, u32 phy_blk)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       u32 page_addr;
+       u8 reg = 0, *ptr;
+       int i, retval;
+
+       if (phy_blk == BLK_NOT_FOUND)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       page_addr = phy_blk << xd_card->block_shift;
+
+       for (i = 0; i < 3; i++) {
+               rtsx_init_cmd(chip);
+
+               xd_assign_phy_addr(chip, page_addr, XD_ERASE_ADDR);
+
+               rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+                       XD_TRANSFER_START | XD_ERASE);
+               rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+                       XD_TRANSFER_END, XD_TRANSFER_END);
+               rtsx_add_cmd(chip, READ_REG_CMD, XD_DAT, 0, 0);
+
+               retval = rtsx_send_cmd(chip, XD_CARD, 250);
+               if (retval < 0) {
+                       rtsx_clear_xd_error(chip);
+                       rtsx_read_register(chip, XD_DAT, &reg);
+                       if (reg & PROGRAM_ERROR) {
+                               xd_mark_bad_block(chip, phy_blk);
+                               xd_set_err_code(chip, XD_PRG_ERROR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       } else {
+                               xd_set_err_code(chip, XD_ERASE_FAIL);
+                       }
+                       retval = xd_reset_cmd(chip);
+                       if (retval != STATUS_SUCCESS)
+                               TRACE_RET(chip, STATUS_FAIL);
+                       continue;
+               }
+
+               ptr = rtsx_get_cmd_data(chip) + 1;
+               if (*ptr & PROGRAM_ERROR) {
+                       xd_mark_bad_block(chip, phy_blk);
+                       xd_set_err_code(chip, XD_PRG_ERROR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               return STATUS_SUCCESS;
+       }
+
+       xd_mark_bad_block(chip, phy_blk);
+       xd_set_err_code(chip, XD_ERASE_FAIL);
+       TRACE_RET(chip, STATUS_FAIL);
+}
+
+
+static int xd_build_l2p_tbl(struct rtsx_chip *chip, int zone_no)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       struct zone_entry *zone;
+       int retval;
+       u32 start, end, i;
+       u16 max_logoff, cur_fst_page_logoff;
+       u16 cur_lst_page_logoff, ent_lst_page_logoff;
+       u8 redunt[11];
+
+       RTSX_DEBUGP("xd_build_l2p_tbl: %d\n", zone_no);
+
+       if (xd_card->zone == NULL) {
+               retval = xd_init_l2p_tbl(chip);
+               if (retval != STATUS_SUCCESS)
+                       return retval;
+       }
+
+       if (xd_card->zone[zone_no].build_flag) {
+               RTSX_DEBUGP("l2p table of zone %d has been built\n", zone_no);
+               return STATUS_SUCCESS;
+       }
+
+       zone = &(xd_card->zone[zone_no]);
+
+       if (zone->l2p_table == NULL) {
+               zone->l2p_table = vmalloc(2000);
+               if (zone->l2p_table == NULL)
+                       TRACE_GOTO(chip, Build_Fail);
+       }
+       memset((u8 *)(zone->l2p_table), 0xff, 2000);
+
+       if (zone->free_table == NULL) {
+               zone->free_table = vmalloc(XD_FREE_TABLE_CNT * 2);
+               if (zone->free_table == NULL)
+                       TRACE_GOTO(chip, Build_Fail);
+       }
+       memset((u8 *)(zone->free_table), 0xff, XD_FREE_TABLE_CNT * 2);
+
+       if (zone_no == 0) {
+               if (xd_card->cis_block == 0xFFFF)
+                       start = 0;
+               else
+                       start = xd_card->cis_block + 1;
+               if (XD_CHK_4MB(xd_card)) {
+                       end = 0x200;
+                       max_logoff = 499;
+               } else {
+                       end = 0x400;
+                       max_logoff = 999;
+               }
+       } else {
+               start = (u32)(zone_no) << 10;
+               end = (u32)(zone_no + 1) << 10;
+               max_logoff = 999;
+       }
+
+       RTSX_DEBUGP("start block 0x%x, end block 0x%x\n", start, end);
+
+       zone->set_index = zone->get_index = 0;
+       zone->unused_blk_cnt = 0;
+
+       for (i = start; i < end; i++) {
+               u32 page_addr = i << xd_card->block_shift;
+               u32 phy_block;
+
+               retval = xd_read_redundant(chip, page_addr, redunt, 11);
+               if (retval != STATUS_SUCCESS)
+                       continue;
+
+               if (redunt[BLOCK_STATUS] != 0xFF) {
+                       RTSX_DEBUGP("bad block\n");
+                       continue;
+               }
+
+               if (xd_check_data_blank(redunt)) {
+                       RTSX_DEBUGP("blank block\n");
+                       xd_set_unused_block(chip, i);
+                       continue;
+               }
+
+               cur_fst_page_logoff = xd_load_log_block_addr(redunt);
+               if ((cur_fst_page_logoff == 0xFFFF) ||
+                       (cur_fst_page_logoff > max_logoff)) {
+                       retval = xd_erase_block(chip, i);
+                       if (retval == STATUS_SUCCESS)
+                               xd_set_unused_block(chip, i);
+                       continue;
+               }
+
+               if ((zone_no == 0) && (cur_fst_page_logoff == 0) &&
+                       (redunt[PAGE_STATUS] != XD_GPG))
+                       XD_SET_MBR_FAIL(xd_card);
+
+               if (zone->l2p_table[cur_fst_page_logoff] == 0xFFFF) {
+                       zone->l2p_table[cur_fst_page_logoff] = (u16)(i & 0x3FF);
+                       continue;
+               }
+
+               phy_block = zone->l2p_table[cur_fst_page_logoff] +
+                       ((u32)((zone_no) << 10));
+
+               page_addr = ((i + 1) << xd_card->block_shift) - 1;
+
+               retval = xd_read_redundant(chip, page_addr, redunt, 11);
+               if (retval != STATUS_SUCCESS)
+                       continue;
+
+               cur_lst_page_logoff = xd_load_log_block_addr(redunt);
+               if (cur_lst_page_logoff == cur_fst_page_logoff) {
+                       int m;
+
+                       page_addr = ((phy_block + 1) <<
+                               xd_card->block_shift) - 1;
+
+                       for (m = 0; m < 3; m++) {
+                               retval = xd_read_redundant(chip, page_addr,
+                                                       redunt, 11);
+                               if (retval == STATUS_SUCCESS)
+                                       break;
+                       }
+
+                       if (m == 3) {
+                               zone->l2p_table[cur_fst_page_logoff] =
+                                       (u16)(i & 0x3FF);
+                               retval = xd_erase_block(chip, phy_block);
+                               if (retval == STATUS_SUCCESS)
+                                       xd_set_unused_block(chip, phy_block);
+                               continue;
+                       }
+
+                       ent_lst_page_logoff = xd_load_log_block_addr(redunt);
+                       if (ent_lst_page_logoff != cur_fst_page_logoff) {
+                               zone->l2p_table[cur_fst_page_logoff] =
+                                       (u16)(i & 0x3FF);
+                               retval = xd_erase_block(chip, phy_block);
+                               if (retval == STATUS_SUCCESS)
+                                       xd_set_unused_block(chip, phy_block);
+                               continue;
+                       } else {
+                               retval = xd_erase_block(chip, i);
+                               if (retval == STATUS_SUCCESS)
+                                       xd_set_unused_block(chip, i);
+                       }
+               } else {
+                       retval = xd_erase_block(chip, i);
+                       if (retval == STATUS_SUCCESS)
+                               xd_set_unused_block(chip, i);
+               }
+       }
+
+       if (XD_CHK_4MB(xd_card))
+               end = 500;
+       else
+               end = 1000;
+
+       i = 0;
+       for (start = 0; start < end; start++) {
+               if (zone->l2p_table[start] == 0xFFFF)
+                       i++;
+       }
+
+       RTSX_DEBUGP("Block count %d, invalid L2P entry %d\n", end, i);
+       RTSX_DEBUGP("Total unused block: %d\n", zone->unused_blk_cnt);
+
+       if ((zone->unused_blk_cnt - i) < 1)
+               chip->card_wp |= XD_CARD;
+
+       zone->build_flag = 1;
+
+       return STATUS_SUCCESS;
+
+Build_Fail:
+       if (zone->l2p_table) {
+               vfree(zone->l2p_table);
+               zone->l2p_table = NULL;
+       }
+       if (zone->free_table) {
+               vfree(zone->free_table);
+               zone->free_table = NULL;
+       }
+
+       return STATUS_FAIL;
+}
+
+static int xd_send_cmd(struct rtsx_chip *chip, u8 cmd)
+{
+       int retval;
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_DAT, 0xFF, cmd);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+               XD_TRANSFER_START | XD_SET_CMD);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+               XD_TRANSFER_END, XD_TRANSFER_END);
+
+       retval = rtsx_send_cmd(chip, XD_CARD, 200);
+       if (retval < 0)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
+
+static int xd_read_multiple_pages(struct rtsx_chip *chip, u32 phy_blk,
+                               u32 log_blk, u8 start_page, u8 end_page,
+                               u8 *buf, unsigned int *index,
+                               unsigned int *offset)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       u32 page_addr, new_blk;
+       u16 log_off;
+       u8 reg_val, page_cnt;
+       int zone_no, retval, i;
+
+       if (start_page > end_page)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       page_cnt = end_page - start_page;
+       zone_no = (int)(log_blk / 1000);
+       log_off = (u16)(log_blk % 1000);
+
+       if ((phy_blk & 0x3FF) == 0x3FF) {
+               for (i = 0; i < 256; i++) {
+                       page_addr = ((u32)i) << xd_card->block_shift;
+
+                       retval = xd_read_redundant(chip, page_addr, NULL, 0);
+                       if (retval == STATUS_SUCCESS)
+                               break;
+
+                       if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+                               xd_set_err_code(chip, XD_NO_CARD);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+       }
+
+       page_addr = (phy_blk << xd_card->block_shift) + start_page;
+
+       rtsx_init_cmd(chip);
+
+       xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, XD_PPB_TO_SIE, XD_PPB_TO_SIE);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, page_cnt);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CHK_DATA_STATUS,
+                       XD_AUTO_CHK_DATA_STATUS, XD_AUTO_CHK_DATA_STATUS);
+
+       trans_dma_enable(chip->srb->sc_data_direction, chip,
+                       page_cnt * 512, DMA_512);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+               XD_TRANSFER_START | XD_READ_PAGES);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+               XD_TRANSFER_END | XD_PPB_EMPTY, XD_TRANSFER_END | XD_PPB_EMPTY);
+
+       rtsx_send_cmd_no_wait(chip);
+
+       retval = rtsx_transfer_data_partial(chip, XD_CARD, buf, page_cnt * 512,
+                                       scsi_sg_count(chip->srb),
+                                       index, offset, DMA_FROM_DEVICE,
+                                       chip->xd_timeout);
+       if (retval < 0) {
+               rtsx_clear_xd_error(chip);
+
+               if (retval == -ETIMEDOUT) {
+                       xd_set_err_code(chip, XD_TO_ERROR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               } else {
+                       TRACE_GOTO(chip, Fail);
+               }
+       }
+
+       return STATUS_SUCCESS;
+
+Fail:
+       RTSX_READ_REG(chip, XD_PAGE_STATUS, &reg_val);
+
+       if (reg_val !=  XD_GPG)
+               xd_set_err_code(chip, XD_PRG_ERROR);
+
+       RTSX_READ_REG(chip, XD_CTL, &reg_val);
+
+       if (((reg_val & (XD_ECC1_ERROR | XD_ECC1_UNCORRECTABLE))
+                               == (XD_ECC1_ERROR | XD_ECC1_UNCORRECTABLE))
+               || ((reg_val & (XD_ECC2_ERROR | XD_ECC2_UNCORRECTABLE))
+                       == (XD_ECC2_ERROR | XD_ECC2_UNCORRECTABLE))) {
+               wait_timeout(100);
+
+               if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+                       xd_set_err_code(chip, XD_NO_CARD);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               xd_set_err_code(chip, XD_ECC_ERROR);
+
+               new_blk = xd_get_unused_block(chip, zone_no);
+               if (new_blk == NO_NEW_BLK) {
+                       XD_CLR_BAD_OLDBLK(xd_card);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               retval = xd_copy_page(chip, phy_blk, new_blk, 0,
+                               xd_card->page_off + 1);
+               if (retval != STATUS_SUCCESS) {
+                       if (!XD_CHK_BAD_NEWBLK(xd_card)) {
+                               retval = xd_erase_block(chip, new_blk);
+                               if (retval == STATUS_SUCCESS)
+                                       xd_set_unused_block(chip, new_blk);
+                       } else {
+                               XD_CLR_BAD_NEWBLK(xd_card);
+                       }
+                       XD_CLR_BAD_OLDBLK(xd_card);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+               xd_set_l2p_tbl(chip, zone_no, log_off, (u16)(new_blk & 0x3FF));
+               xd_erase_block(chip, phy_blk);
+               xd_mark_bad_block(chip, phy_blk);
+               XD_CLR_BAD_OLDBLK(xd_card);
+       }
+
+       TRACE_RET(chip, STATUS_FAIL);
+}
+
+static int xd_finish_write(struct rtsx_chip *chip,
+               u32 old_blk, u32 new_blk, u32 log_blk, u8 page_off)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       int retval, zone_no;
+       u16 log_off;
+
+       RTSX_DEBUGP("xd_finish_write, old_blk = 0x%x, new_blk = 0x%x, log_blk = 0x%x\n",
+                               old_blk, new_blk, log_blk);
+
+       if (page_off > xd_card->page_off)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       zone_no = (int)(log_blk / 1000);
+       log_off = (u16)(log_blk % 1000);
+
+       if (old_blk == BLK_NOT_FOUND) {
+               retval = xd_init_page(chip, new_blk, log_off,
+                               page_off, xd_card->page_off + 1);
+               if (retval != STATUS_SUCCESS) {
+                       retval = xd_erase_block(chip, new_blk);
+                       if (retval == STATUS_SUCCESS)
+                               xd_set_unused_block(chip, new_blk);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       } else {
+               retval = xd_copy_page(chip, old_blk, new_blk,
+                               page_off, xd_card->page_off + 1);
+               if (retval != STATUS_SUCCESS) {
+                       if (!XD_CHK_BAD_NEWBLK(xd_card)) {
+                               retval = xd_erase_block(chip, new_blk);
+                               if (retval == STATUS_SUCCESS)
+                                       xd_set_unused_block(chip, new_blk);
+                       }
+                       XD_CLR_BAD_NEWBLK(xd_card);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               retval = xd_erase_block(chip, old_blk);
+               if (retval == STATUS_SUCCESS) {
+                       if (XD_CHK_BAD_OLDBLK(xd_card)) {
+                               xd_mark_bad_block(chip, old_blk);
+                               XD_CLR_BAD_OLDBLK(xd_card);
+                       } else {
+                               xd_set_unused_block(chip, old_blk);
+                       }
+               } else {
+                       xd_set_err_code(chip, XD_NO_ERROR);
+                       XD_CLR_BAD_OLDBLK(xd_card);
+               }
+       }
+
+       xd_set_l2p_tbl(chip, zone_no, log_off, (u16)(new_blk & 0x3FF));
+
+       return STATUS_SUCCESS;
+}
+
+static int xd_prepare_write(struct rtsx_chip *chip,
+               u32 old_blk, u32 new_blk, u32 log_blk, u8 page_off)
+{
+       int retval;
+
+       RTSX_DEBUGP("%s, old_blk = 0x%x, new_blk = 0x%x, log_blk = 0x%x, page_off = %d\n",
+               __func__, old_blk, new_blk, log_blk, (int)page_off);
+
+       if (page_off) {
+               retval = xd_copy_page(chip, old_blk, new_blk, 0, page_off);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+
+static int xd_write_multiple_pages(struct rtsx_chip *chip, u32 old_blk,
+                               u32 new_blk, u32 log_blk, u8 start_page,
+                               u8 end_page, u8 *buf, unsigned int *index,
+                               unsigned int *offset)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       u32 page_addr;
+       int zone_no, retval;
+       u16 log_off;
+       u8 page_cnt, reg_val;
+
+       RTSX_DEBUGP("%s, old_blk = 0x%x, new_blk = 0x%x, log_blk = 0x%x\n",
+                               __func__, old_blk, new_blk, log_blk);
+
+       if (start_page > end_page)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       page_cnt = end_page - start_page;
+       zone_no = (int)(log_blk / 1000);
+       log_off = (u16)(log_blk % 1000);
+
+       page_addr = (new_blk << xd_card->block_shift) + start_page;
+
+       retval = xd_send_cmd(chip, READ1_1);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       rtsx_init_cmd(chip);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_H,
+               0xFF, (u8)(log_off >> 8));
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_L, 0xFF, (u8)log_off);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_STATUS, 0xFF, XD_GBLK);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_STATUS, 0xFF, XD_GPG);
+
+       xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, XD_BA_TRANSFORM,
+               XD_BA_TRANSFORM);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, page_cnt);
+       rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+
+       trans_dma_enable(chip->srb->sc_data_direction, chip,
+                       page_cnt * 512, DMA_512);
+
+       rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER,
+               0xFF, XD_TRANSFER_START | XD_WRITE_PAGES);
+       rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+               XD_TRANSFER_END, XD_TRANSFER_END);
+
+       rtsx_send_cmd_no_wait(chip);
+
+       retval = rtsx_transfer_data_partial(chip, XD_CARD, buf, page_cnt * 512,
+                                       scsi_sg_count(chip->srb),
+                       index, offset, DMA_TO_DEVICE, chip->xd_timeout);
+       if (retval < 0) {
+               rtsx_clear_xd_error(chip);
+
+               if (retval == -ETIMEDOUT) {
+                       xd_set_err_code(chip, XD_TO_ERROR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               } else {
+                       TRACE_GOTO(chip, Fail);
+               }
+       }
+
+       if (end_page == (xd_card->page_off + 1)) {
+               xd_card->delay_write.delay_write_flag = 0;
+
+               if (old_blk != BLK_NOT_FOUND) {
+                       retval = xd_erase_block(chip, old_blk);
+                       if (retval == STATUS_SUCCESS) {
+                               if (XD_CHK_BAD_OLDBLK(xd_card)) {
+                                       xd_mark_bad_block(chip, old_blk);
+                                       XD_CLR_BAD_OLDBLK(xd_card);
+                               } else {
+                                       xd_set_unused_block(chip, old_blk);
+                               }
+                       } else {
+                               xd_set_err_code(chip, XD_NO_ERROR);
+                               XD_CLR_BAD_OLDBLK(xd_card);
+                       }
+               }
+               xd_set_l2p_tbl(chip, zone_no, log_off, (u16)(new_blk & 0x3FF));
+       }
+
+       return STATUS_SUCCESS;
+
+Fail:
+       RTSX_READ_REG(chip, XD_DAT, &reg_val);
+       if (reg_val & PROGRAM_ERROR) {
+               xd_set_err_code(chip, XD_PRG_ERROR);
+               xd_mark_bad_block(chip, new_blk);
+       }
+
+       TRACE_RET(chip, STATUS_FAIL);
+}
+
+#ifdef XD_DELAY_WRITE
+int xd_delay_write(struct rtsx_chip *chip)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       struct xd_delay_write_tag *delay_write = &(xd_card->delay_write);
+       int retval;
+
+       if (delay_write->delay_write_flag) {
+               RTSX_DEBUGP("xd_delay_write\n");
+               retval = xd_switch_clock(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               delay_write->delay_write_flag = 0;
+               retval = xd_finish_write(chip,
+                               delay_write->old_phyblock,
+                                       delay_write->new_phyblock,
+                               delay_write->logblock, delay_write->pageoff);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       return STATUS_SUCCESS;
+}
+#endif
+
+int xd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+       u32 start_sector, u16 sector_cnt)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       unsigned int lun = SCSI_LUN(srb);
+#ifdef XD_DELAY_WRITE
+       struct xd_delay_write_tag *delay_write = &(xd_card->delay_write);
+#endif
+       int retval, zone_no;
+       unsigned int index = 0, offset = 0;
+       u32 log_blk, old_blk = 0, new_blk = 0;
+       u16 log_off, total_sec_cnt = sector_cnt;
+       u8 start_page, end_page = 0, page_cnt;
+       u8 *ptr;
+
+       xd_set_err_code(chip, XD_NO_ERROR);
+
+       xd_card->cleanup_counter = 0;
+
+       RTSX_DEBUGP("xd_rw: scsi_sg_count = %d\n", scsi_sg_count(srb));
+
+       ptr = (u8 *)scsi_sglist(srb);
+
+       retval = xd_switch_clock(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+
+       if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+               chip->card_fail |= XD_CARD;
+               set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+               TRACE_RET(chip, STATUS_FAIL);
+       }
+
+       log_blk = start_sector >> xd_card->block_shift;
+       start_page = (u8)start_sector & xd_card->page_off;
+       zone_no = (int)(log_blk / 1000);
+       log_off = (u16)(log_blk % 1000);
+
+       if (xd_card->zone[zone_no].build_flag == 0) {
+               retval = xd_build_l2p_tbl(chip, zone_no);
+               if (retval != STATUS_SUCCESS) {
+                       chip->card_fail |= XD_CARD;
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       if (srb->sc_data_direction == DMA_TO_DEVICE) {
+#ifdef XD_DELAY_WRITE
+               if (delay_write->delay_write_flag &&
+                               (delay_write->logblock == log_blk) &&
+                               (start_page > delay_write->pageoff)) {
+                       delay_write->delay_write_flag = 0;
+                       if (delay_write->old_phyblock != BLK_NOT_FOUND) {
+                               retval = xd_copy_page(chip,
+                                       delay_write->old_phyblock,
+                                       delay_write->new_phyblock,
+                                       delay_write->pageoff, start_page);
+                               if (retval != STATUS_SUCCESS) {
+                                       set_sense_type(chip, lun,
+                                               SENSE_TYPE_MEDIA_WRITE_ERR);
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               }
+                       }
+                       old_blk = delay_write->old_phyblock;
+                       new_blk = delay_write->new_phyblock;
+               } else if (delay_write->delay_write_flag &&
+                               (delay_write->logblock == log_blk) &&
+                               (start_page == delay_write->pageoff)) {
+                       delay_write->delay_write_flag = 0;
+                       old_blk = delay_write->old_phyblock;
+                       new_blk = delay_write->new_phyblock;
+               } else {
+                       retval = xd_delay_write(chip);
+                       if (retval != STATUS_SUCCESS) {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+#endif
+                       old_blk = xd_get_l2p_tbl(chip, zone_no, log_off);
+                       new_blk  = xd_get_unused_block(chip, zone_no);
+                       if ((old_blk == BLK_NOT_FOUND) ||
+                               (new_blk == BLK_NOT_FOUND)) {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+
+                       retval = xd_prepare_write(chip, old_blk, new_blk,
+                                               log_blk, start_page);
+                       if (retval != STATUS_SUCCESS) {
+                               if (detect_card_cd(chip, XD_CARD) !=
+                                       STATUS_SUCCESS) {
+                                       set_sense_type(chip, lun,
+                                               SENSE_TYPE_MEDIA_NOT_PRESENT);
+                                       TRACE_RET(chip, STATUS_FAIL);
+                               }
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+#ifdef XD_DELAY_WRITE
+               }
+#endif
+       } else {
+#ifdef XD_DELAY_WRITE
+               retval = xd_delay_write(chip);
+               if (retval != STATUS_SUCCESS) {
+                       if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_NOT_PRESENT);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+#endif
+
+               old_blk = xd_get_l2p_tbl(chip, zone_no, log_off);
+               if (old_blk == BLK_NOT_FOUND) {
+                       set_sense_type(chip, lun,
+                               SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+       }
+
+       RTSX_DEBUGP("old_blk = 0x%x\n", old_blk);
+
+       while (total_sec_cnt) {
+               if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+                       chip->card_fail |= XD_CARD;
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               if ((start_page + total_sec_cnt) > (xd_card->page_off + 1))
+                       end_page = xd_card->page_off + 1;
+               else
+                       end_page = start_page + (u8)total_sec_cnt;
+
+               page_cnt = end_page - start_page;
+               if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+                       retval = xd_read_multiple_pages(chip, old_blk, log_blk,
+                                       start_page, end_page, ptr,
+                                                       &index, &offset);
+                       if (retval != STATUS_SUCCESS) {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               } else {
+                       retval = xd_write_multiple_pages(chip, old_blk,
+                                                       new_blk, log_blk,
+                                       start_page, end_page, ptr,
+                                                       &index, &offset);
+                       if (retval != STATUS_SUCCESS) {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+
+               total_sec_cnt -= page_cnt;
+               if (scsi_sg_count(srb) == 0)
+                       ptr += page_cnt * 512;
+
+               if (total_sec_cnt == 0)
+                       break;
+
+               log_blk++;
+               zone_no = (int)(log_blk / 1000);
+               log_off = (u16)(log_blk % 1000);
+
+               if (xd_card->zone[zone_no].build_flag == 0) {
+                       retval = xd_build_l2p_tbl(chip, zone_no);
+                       if (retval != STATUS_SUCCESS) {
+                               chip->card_fail |= XD_CARD;
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_NOT_PRESENT);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+
+               old_blk = xd_get_l2p_tbl(chip, zone_no, log_off);
+               if (old_blk == BLK_NOT_FOUND) {
+                       if (srb->sc_data_direction == DMA_FROM_DEVICE)
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+                       else
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               if (srb->sc_data_direction == DMA_TO_DEVICE) {
+                       new_blk = xd_get_unused_block(chip, zone_no);
+                       if (new_blk == BLK_NOT_FOUND) {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_WRITE_ERR);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+               }
+
+               start_page = 0;
+       }
+
+       if ((srb->sc_data_direction == DMA_TO_DEVICE) &&
+                       (end_page != (xd_card->page_off + 1))) {
+#ifdef XD_DELAY_WRITE
+               delay_write->delay_write_flag = 1;
+               delay_write->old_phyblock = old_blk;
+               delay_write->new_phyblock = new_blk;
+               delay_write->logblock = log_blk;
+               delay_write->pageoff = end_page;
+#else
+               if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+                       chip->card_fail |= XD_CARD;
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+
+               retval = xd_finish_write(chip, old_blk, new_blk,
+                                       log_blk, end_page);
+               if (retval != STATUS_SUCCESS) {
+                       if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+                               set_sense_type(chip, lun,
+                                       SENSE_TYPE_MEDIA_NOT_PRESENT);
+                               TRACE_RET(chip, STATUS_FAIL);
+                       }
+                       set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
+                       TRACE_RET(chip, STATUS_FAIL);
+               }
+#endif
+       }
+
+       scsi_set_resid(srb, 0);
+
+       return STATUS_SUCCESS;
+}
+
+void xd_free_l2p_tbl(struct rtsx_chip *chip)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       int i = 0;
+
+       if (xd_card->zone != NULL) {
+               for (i = 0; i < xd_card->zone_cnt; i++) {
+                       if (xd_card->zone[i].l2p_table != NULL) {
+                               vfree(xd_card->zone[i].l2p_table);
+                               xd_card->zone[i].l2p_table = NULL;
+                       }
+                       if (xd_card->zone[i].free_table != NULL) {
+                               vfree(xd_card->zone[i].free_table);
+                               xd_card->zone[i].free_table = NULL;
+                       }
+               }
+               vfree(xd_card->zone);
+               xd_card->zone = NULL;
+       }
+}
+
+void xd_cleanup_work(struct rtsx_chip *chip)
+{
+#ifdef XD_DELAY_WRITE
+       struct xd_info *xd_card = &(chip->xd_card);
+
+       if (xd_card->delay_write.delay_write_flag) {
+               RTSX_DEBUGP("xD: delay write\n");
+               xd_delay_write(chip);
+               xd_card->cleanup_counter = 0;
+       }
+#endif
+}
+
+int xd_power_off_card3v3(struct rtsx_chip *chip)
+{
+       int retval;
+
+       retval = disable_card_clock(chip, XD_CARD);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       RTSX_WRITE_REG(chip, CARD_OE, XD_OUTPUT_EN, 0);
+
+       if (!chip->ft2_fast_mode) {
+               retval = card_power_off(chip, XD_CARD);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+
+               wait_timeout(50);
+       }
+
+       if (chip->asic_code) {
+               retval = xd_pull_ctl_disable(chip);
+               if (retval != STATUS_SUCCESS)
+                       TRACE_RET(chip, STATUS_FAIL);
+       } else {
+               RTSX_WRITE_REG(chip, FPGA_PULL_CTL, 0xFF, 0xDF);
+       }
+
+       return STATUS_SUCCESS;
+}
+
+int release_xd_card(struct rtsx_chip *chip)
+{
+       struct xd_info *xd_card = &(chip->xd_card);
+       int retval;
+
+       RTSX_DEBUGP("release_xd_card\n");
+
+       chip->card_ready &= ~XD_CARD;
+       chip->card_fail &= ~XD_CARD;
+       chip->card_wp &= ~XD_CARD;
+
+       xd_card->delay_write.delay_write_flag = 0;
+
+       xd_free_l2p_tbl(chip);
+
+       retval = xd_power_off_card3v3(chip);
+       if (retval != STATUS_SUCCESS)
+               TRACE_RET(chip, STATUS_FAIL);
+
+       return STATUS_SUCCESS;
+}
diff --git a/drivers/staging/rts5208/xd.h b/drivers/staging/rts5208/xd.h
new file mode 100644 (file)
index 0000000..938138c
--- /dev/null
@@ -0,0 +1,188 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG (wei_wang@realsil.com.cn)
+ *   Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_XD_H
+#define __REALTEK_RTSX_XD_H
+
+#define        XD_DELAY_WRITE
+
+/* Error Codes */
+#define        XD_NO_ERROR                     0x00
+#define        XD_NO_MEMORY                    0x80
+#define        XD_PRG_ERROR                    0x40
+#define        XD_NO_CARD                      0x20
+#define        XD_READ_FAIL                    0x10
+#define        XD_ERASE_FAIL                   0x08
+#define        XD_WRITE_FAIL                   0x04
+#define        XD_ECC_ERROR                    0x02
+#define        XD_TO_ERROR                     0x01
+
+/* XD Commands */
+#define        READ1_1                         0x00
+#define        READ1_2                         0x01
+#define        READ2                           0x50
+#define READ_ID                                0x90
+#define RESET                          0xff
+#define PAGE_PRG_1                     0x80
+#define PAGE_PRG_2                     0x10
+#define        BLK_ERASE_1                     0x60
+#define        BLK_ERASE_2                     0xD0
+#define READ_STS                       0x70
+#define READ_xD_ID                     0x9A
+#define        COPY_BACK_512                   0x8A
+#define        COPY_BACK_2K                    0x85
+#define        READ1_1_2                       0x30
+#define        READ1_1_3                       0x35
+#define        CHG_DAT_OUT_1                   0x05
+#define RDM_DAT_OUT_1                  0x05
+#define        CHG_DAT_OUT_2                   0xE0
+#define RDM_DAT_OUT_2                  0xE0
+#define        CHG_DAT_OUT_2                   0xE0
+#define        CHG_DAT_IN_1                    0x85
+#define        CACHE_PRG                       0x15
+
+/* Redundant Area Related */
+#define XD_EXTRA_SIZE                  0x10
+#define XD_2K_EXTRA_SIZE               0x40
+
+#define        NOT_WRITE_PROTECTED             0x80
+#define        READY_STATE                     0x40
+#define        PROGRAM_ERROR                   0x01
+#define        PROGRAM_ERROR_N_1               0x02
+#define        INTERNAL_READY                  0x20
+#define        READY_FLAG                      0x5F
+
+#define        XD_8M_X8_512                    0xE6
+#define        XD_16M_X8_512                   0x73
+#define        XD_32M_X8_512                   0x75
+#define        XD_64M_X8_512                   0x76
+#define        XD_128M_X8_512                  0x79
+#define        XD_256M_X8_512                  0x71
+#define        XD_128M_X8_2048                 0xF1
+#define        XD_256M_X8_2048                 0xDA
+#define        XD_512M_X8                      0xDC
+#define        XD_128M_X16_2048                0xC1
+#define        XD_4M_X8_512_1                  0xE3
+#define        XD_4M_X8_512_2                  0xE5
+#define        xD_1G_X8_512                    0xD3
+#define        xD_2G_X8_512                    0xD5
+
+#define        XD_ID_CODE                      0xB5
+
+#define        VENDOR_BLOCK                    0xEFFF
+#define        CIS_BLOCK                       0xDFFF
+
+#define        BLK_NOT_FOUND                   0xFFFFFFFF
+
+#define        NO_NEW_BLK                      0xFFFFFFFF
+
+#define        PAGE_CORRECTABLE                0x0
+#define        PAGE_NOTCORRECTABLE             0x1
+
+#define        NO_OFFSET                       0x0
+#define        WITH_OFFSET                     0x1
+
+#define        Sect_Per_Page                   4
+#define        XD_ADDR_MODE_2C                 XD_ADDR_MODE_2A
+
+#define ZONE0_BAD_BLOCK                        23
+#define NOT_ZONE0_BAD_BLOCK            24
+
+#define        XD_RW_ADDR                      0x01
+#define        XD_ERASE_ADDR                   0x02
+
+#define        XD_PAGE_512(xd_card)            \
+do {                                   \
+       (xd_card)->block_shift = 5;     \
+       (xd_card)->page_off = 0x1F;     \
+} while (0)
+
+#define        XD_SET_BAD_NEWBLK(xd_card)      ((xd_card)->multi_flag |= 0x01)
+#define        XD_CLR_BAD_NEWBLK(xd_card)      ((xd_card)->multi_flag &= ~0x01)
+#define        XD_CHK_BAD_NEWBLK(xd_card)      ((xd_card)->multi_flag & 0x01)
+
+#define        XD_SET_BAD_OLDBLK(xd_card)      ((xd_card)->multi_flag |= 0x02)
+#define        XD_CLR_BAD_OLDBLK(xd_card)      ((xd_card)->multi_flag &= ~0x02)
+#define        XD_CHK_BAD_OLDBLK(xd_card)      ((xd_card)->multi_flag & 0x02)
+
+#define        XD_SET_MBR_FAIL(xd_card)        ((xd_card)->multi_flag |= 0x04)
+#define        XD_CLR_MBR_FAIL(xd_card)        ((xd_card)->multi_flag &= ~0x04)
+#define        XD_CHK_MBR_FAIL(xd_card)        ((xd_card)->multi_flag & 0x04)
+
+#define        XD_SET_ECC_FLD_ERR(xd_card)     ((xd_card)->multi_flag |= 0x08)
+#define        XD_CLR_ECC_FLD_ERR(xd_card)     ((xd_card)->multi_flag &= ~0x08)
+#define        XD_CHK_ECC_FLD_ERR(xd_card)     ((xd_card)->multi_flag & 0x08)
+
+#define        XD_SET_4MB(xd_card)             ((xd_card)->multi_flag |= 0x10)
+#define        XD_CLR_4MB(xd_card)             ((xd_card)->multi_flag &= ~0x10)
+#define        XD_CHK_4MB(xd_card)             ((xd_card)->multi_flag & 0x10)
+
+#define        XD_SET_ECC_ERR(xd_card)         ((xd_card)->multi_flag |= 0x40)
+#define        XD_CLR_ECC_ERR(xd_card)         ((xd_card)->multi_flag &= ~0x40)
+#define        XD_CHK_ECC_ERR(xd_card)         ((xd_card)->multi_flag & 0x40)
+
+#define PAGE_STATUS            0
+#define BLOCK_STATUS           1
+#define BLOCK_ADDR1_L          2
+#define BLOCK_ADDR1_H          3
+#define BLOCK_ADDR2_L          4
+#define BLOCK_ADDR2_H          5
+#define RESERVED0              6
+#define RESERVED1              7
+#define RESERVED2              8
+#define RESERVED3              9
+#define PARITY                 10
+
+#define        CIS0_0                  0
+#define        CIS0_1                  1
+#define        CIS0_2                  2
+#define        CIS0_3                  3
+#define        CIS0_4                  4
+#define        CIS0_5                  5
+#define        CIS0_6                  6
+#define        CIS0_7                  7
+#define        CIS0_8                  8
+#define        CIS0_9                  9
+#define        CIS1_0                  256
+#define        CIS1_1                  (256 + 1)
+#define        CIS1_2                  (256 + 2)
+#define        CIS1_3                  (256 + 3)
+#define        CIS1_4                  (256 + 4)
+#define        CIS1_5                  (256 + 5)
+#define        CIS1_6                  (256 + 6)
+#define        CIS1_7                  (256 + 7)
+#define        CIS1_8                  (256 + 8)
+#define        CIS1_9                  (256 + 9)
+
+int reset_xd_card(struct rtsx_chip *chip);
+#ifdef XD_DELAY_WRITE
+int xd_delay_write(struct rtsx_chip *chip);
+#endif
+int xd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+       u32 start_sector, u16 sector_cnt);
+void xd_free_l2p_tbl(struct rtsx_chip *chip);
+void xd_cleanup_work(struct rtsx_chip *chip);
+int xd_power_off_card3v3(struct rtsx_chip *chip);
+int release_xd_card(struct rtsx_chip *chip);
+
+#endif  /* __REALTEK_RTSX_XD_H */