Commit 77d89b08 authored by wwang's avatar wwang Committed by Greg Kroah-Hartman

staging: add rts_pstor for Realtek PCIE cardreader

rts_pstor is used to support Realtek PCI-E card readers,
including rts5209, rts5208, Barossa.
Signed-off-by: default avatarwwang <wei_wang@realsil.com.cn>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 88c8a443
...@@ -87,6 +87,8 @@ source "drivers/staging/rtl8192e/Kconfig" ...@@ -87,6 +87,8 @@ source "drivers/staging/rtl8192e/Kconfig"
source "drivers/staging/rtl8712/Kconfig" source "drivers/staging/rtl8712/Kconfig"
source "drivers/staging/rts_pstor/Kconfig"
source "drivers/staging/frontier/Kconfig" source "drivers/staging/frontier/Kconfig"
source "drivers/staging/pohmelfs/Kconfig" source "drivers/staging/pohmelfs/Kconfig"
......
...@@ -27,6 +27,7 @@ obj-$(CONFIG_R8187SE) += rtl8187se/ ...@@ -27,6 +27,7 @@ obj-$(CONFIG_R8187SE) += rtl8187se/
obj-$(CONFIG_RTL8192U) += rtl8192u/ obj-$(CONFIG_RTL8192U) += rtl8192u/
obj-$(CONFIG_RTL8192E) += rtl8192e/ obj-$(CONFIG_RTL8192E) += rtl8192e/
obj-$(CONFIG_R8712U) += rtl8712/ obj-$(CONFIG_R8712U) += rtl8712/
obj-$(CONFIG_RTS_PSTOR) += rts_pstor/
obj-$(CONFIG_SPECTRA) += spectra/ obj-$(CONFIG_SPECTRA) += spectra/
obj-$(CONFIG_TRANZPORT) += frontier/ obj-$(CONFIG_TRANZPORT) += frontier/
obj-$(CONFIG_POHMELFS) += pohmelfs/ obj-$(CONFIG_POHMELFS) += pohmelfs/
......
config RTS_PSTOR
tristate "RealTek PCI-E Card Reader support"
help
Say Y here to include driver code to support the Realtek
PCI-E card readers.
If this driver is compiled as a module, it will be named rts_pstor.
config RTS_PSTOR_DEBUG
bool "Realtek PCI-E Card Reader verbose debug"
depends on RTS_PSTOR
help
Say Y here in order to have the rts_pstor code generate
verbose debugging messages.
EXTRA_CFLAGS := -Idrivers/scsi
obj-$(CONFIG_RTS_PSTOR) := rts_pstor.o
rts_pstor-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
TODO:
- support more pcie card reader of Realtek family
- use kernel coding style
- checkpatch.pl fixes
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#ifndef __REALTEK_RTSX_DEBUG_H
#define __REALTEK_RTSX_DEBUG_H
#include <linux/kernel.h>
#define RTSX_STOR "rts_pstor: "
#if CONFIG_RTS_PSTOR_DEBUG
#define RTSX_DEBUGP(x...) printk(KERN_DEBUG RTSX_STOR x)
#define RTSX_DEBUGPN(x...) printk(KERN_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 */
/* Driver for Realtek PCI-Express card reader
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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;
}
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#ifndef __RTSX_GENERAL_H
#define __RTSX_GENERAL_H
#include "rtsx.h"
int bit1cnt_long(u32 data);
#endif /* __RTSX_GENERAL_H */
This source diff could not be displayed because it is too large. You can view the blob instead.
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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 */
/* Driver for Realtek PCI-Express card reader
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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"
#define DRIVER_VERSION "v1.10"
MODULE_DESCRIPTION("Realtek PCI-Express card reader driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRIVER_VERSION);
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");
/* These are used to make sure the module doesn't unload before all the
* threads have exited.
*/
static atomic_t total_threads = ATOMIC_INIT(0);
static DECLARE_COMPLETION(threads_gone);
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)
static int proc_info (struct Scsi_Host *host, char *buffer,
char **start, off_t offset, int length, int inout)
{
char *pos = buffer;
/* if someone is sending us data, just throw it away */
if (inout)
return length;
/* print the controller name */
SPRINTF(" Host scsi%d: %s\n", host->host_no, CR_DRIVER_NAME);
/* print product, vendor, and driver version strings */
SPRINTF(" Vendor: Realtek Corp.\n");
SPRINTF(" Product: PCIE Card Reader\n");
SPRINTF(" Version: %s\n", DRIVER_VERSION);
/*
* Calculate start of next buffer, and return value.
*/
*start = buffer + offset;
if ((pos - buffer) < offset)
return 0;
else if ((pos - buffer - offset) < length)
return pos - buffer - offset;
else
return length;
}
/* 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) {
printk(KERN_ERR "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)) {
printk(KERN_INFO "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;
up(&(dev->sema));
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;
printk(KERN_INFO "%s called\n", __func__);
scsi_lock(host);
/* Is this command still active? */
if (chip->srb != srb) {
scsi_unlock(host);
printk(KERN_INFO "-- 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;
printk(KERN_INFO "%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;
printk(KERN_INFO "%s called\n", __func__);
return result < 0 ? FAILED : SUCCESS;
}
/*
* this defines our host template, with which we'll allocate hosts
*/
struct scsi_host_template rtsx_host_template = {
/* basic userland interface stuff */
.name = CR_DRIVER_NAME,
.proc_name = CR_DRIVER_NAME,
.proc_info = proc_info,
.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;
printk(KERN_INFO "%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)) {
printk(KERN_ERR "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 (!dev)
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;
printk(KERN_INFO "Ready to suspend\n");
if (!dev) {
printk(KERN_ERR "Invalid memory\n");
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;
printk(KERN_INFO "Ready to resume\n");
if (!dev) {
printk(KERN_ERR "Invalid memory\n");
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) {
printk(KERN_ERR "%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 */
void rtsx_shutdown(struct pci_dev *pci)
{
struct rtsx_dev *dev = (struct rtsx_dev *)pci_get_drvdata(pci);
struct rtsx_chip *chip;
printk(KERN_INFO "Ready to shutdown\n");
if (!dev) {
printk(KERN_ERR "Invalid memory\n");
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);
current->flags |= PF_NOFREEZE;
for (;;) {
if (down_interruptible(&dev->sema))
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)) {
printk(KERN_INFO "-- 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) {
printk(KERN_ERR "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) {
printk(KERN_ERR "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) {
printk(KERN_ERR "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:
printk(KERN_ERR "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 (;;) */
scsi_host_put(host);
/* 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(&threads_gone, 0);
}
static int rtsx_polling_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);
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 (;;) {
wait_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)) {
printk(KERN_INFO "-- 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);
}
scsi_host_put(host);
complete_and_exit(&threads_gone, 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)
{
printk(KERN_INFO "-- %s\n", __func__);
if (dev->rtsx_resv_buf) {
dma_free_coherent(&(dev->pci->dev), HOST_CMDS_BUF_LEN,
dev->rtsx_resv_buf, dev->rtsx_resv_buf_addr);
dev->chip->host_cmds_ptr = NULL;
dev->chip->host_sg_tbl_ptr = NULL;
}
pci_disable_device(dev->pci);
pci_release_regions(dev->pci);
if (dev->irq > 0) {
free_irq(dev->irq, (void *)dev);
}
if (dev->chip->msi_en) {
pci_disable_msi(dev->pci);
}
/* Tell the control thread to exit. The SCSI host must
* already have been removed so it won't try to queue
* any more commands.
*/
printk(KERN_INFO "-- sending exit command to thread\n");
up(&dev->sema);
}
/* 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 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) {
printk(KERN_INFO "%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));
printk(KERN_INFO "%s: device scan complete\n", CR_DRIVER_NAME);
/* Should we unbind if no devices were detected? */
}
scsi_host_put(rtsx_to_host(dev));
complete_and_exit(&threads_gone, 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 = 207;
chip->asic_sd_sdr50_clk = 99;
chip->asic_sd_ddr50_clk = 99;
chip->asic_sd_hs_clk = 99;
chip->asic_mmc_52m_clk = 99;
chip->asic_ms_hg_clk = 119;
chip->asic_ms_4bit_clk = 79;
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->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 __devinit 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) {
printk(KERN_ERR "PCI enable device failed!\n");
return err;
}
err = pci_request_regions(pci, CR_DRIVER_NAME);
if (err < 0) {
printk(KERN_ERR "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) {
printk(KERN_ERR "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 = (struct rtsx_chip *)kmalloc(sizeof(struct rtsx_chip), GFP_KERNEL);
if (dev->chip == NULL) {
goto errout;
}
memset(dev->chip, 0, sizeof(struct rtsx_chip));
spin_lock_init(&dev->reg_lock);
mutex_init(&(dev->dev_mutex));
sema_init(&(dev->sema), 0);
init_completion(&(dev->notify));
init_waitqueue_head(&dev->delay_wait);
dev->pci = pci;
dev->irq = -1;
printk(KERN_INFO "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) {
printk(KERN_ERR "ioremap error\n");
err = -ENXIO;
goto errout;
}
/* Using "unsigned long" cast here to eliminate gcc warning in 64-bit system */
printk(KERN_INFO "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) {
printk(KERN_ERR "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);
printk(KERN_INFO "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);
err = scsi_add_host(host, &pci->dev);
if (err) {
printk(KERN_ERR "Unable to add the scsi host\n");
goto errout;
}
rtsx_init_chip(dev->chip);
/* Start up our control thread */
th = kthread_create(rtsx_control_thread, dev, CR_DRIVER_NAME);
if (IS_ERR(th)) {
printk(KERN_ERR "Unable to start control thread\n");
err = PTR_ERR(th);
goto errout;
}
/* Take a reference to the host for the control thread and
* count it among all the threads we have launched. Then
* start it up. */
scsi_host_get(rtsx_to_host(dev));
atomic_inc(&total_threads);
wake_up_process(th);
/* Start up the thread for delayed SCSI-device scanning */
th = kthread_create(rtsx_scan_thread, dev, "rtsx-scan");
if (IS_ERR(th)) {
printk(KERN_ERR "Unable to start the device-scanning thread\n");
quiesce_and_remove_host(dev);
err = PTR_ERR(th);
goto errout;
}
/* Take a reference to the host for the scanning thread and
* count it among all the threads we have launched. Then
* start it up. */
scsi_host_get(rtsx_to_host(dev));
atomic_inc(&total_threads);
wake_up_process(th);
/* Start up the thread for polling thread */
th = kthread_create(rtsx_polling_thread, dev, "rtsx-polling");
if (IS_ERR(th)) {
printk(KERN_ERR "Unable to start the device-polling thread\n");
quiesce_and_remove_host(dev);
err = PTR_ERR(th);
goto errout;
}
/* Take a reference to the host for the polling thread and
* count it among all the threads we have launched. Then
* start it up. */
scsi_host_get(rtsx_to_host(dev));
atomic_inc(&total_threads);
wake_up_process(th);
pci_set_drvdata(pci, dev);
return 0;
/* We come here if there are any problems */
errout:
printk(KERN_ERR "rtsx_probe() failed\n");
release_everything(dev);
return err;
}
static void __devexit rtsx_remove(struct pci_dev *pci)
{
struct rtsx_dev *dev = (struct rtsx_dev *)pci_get_drvdata(pci);
printk(KERN_INFO "rtsx_remove() called\n");
quiesce_and_remove_host(dev);
release_everything(dev);
pci_set_drvdata(pci, NULL);
}
/* PCI IDs */
static struct pci_device_id rtsx_ids[] = {
{ 0x10EC, 0x5208, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ 0x10EC, 0x5209, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_OTHERS << 16, 0xFF0000 },
{ 0x10EC, 0x5288, PCI_ANY_ID, PCI_ANY_ID, 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 = __devexit_p(rtsx_remove),
#ifdef CONFIG_PM
.suspend = rtsx_suspend,
.resume = rtsx_resume,
#endif
.shutdown = rtsx_shutdown,
};
static int __init rtsx_init(void)
{
printk(KERN_INFO "Initializing Realtek PCIE storage driver...\n");
return pci_register_driver(&driver);
}
static void __exit rtsx_exit(void)
{
printk(KERN_INFO "rtsx_exit() called\n");
pci_unregister_driver(&driver);
/* Don't return until all of our control and scanning threads
* have exited. Since each thread signals threads_gone as its
* last act, we have to call wait_for_completion the right number
* of times.
*/
while (atomic_read(&total_threads) > 0) {
wait_for_completion(&threads_gone);
atomic_dec(&total_threads);
}
printk(KERN_INFO "%s module exit\n", CR_DRIVER_NAME);
}
module_init(rtsx_init)
module_exit(rtsx_exit)
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#ifndef __REALTEK_RTSX_H
#define __REALTEK_RTSX_H
#include <asm/io.h>
#include <asm/bitops.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/version.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 "rts_pstor"
#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)
#define rtsx_alloc_dma_buf(chip, size, flag) kmalloc((size), (flag))
#define rtsx_free_dma_buf(chip, ptr) kfree((ptr))
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;
/* mutual exclusion and synchronization structures */
struct semaphore sema; /* to sleep thread on */
struct completion notify; /* thread begin/end */
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 */
/* Driver for Realtek PCI-Express card reader
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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);
}
}
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 (CHECK_PID(chip, 0x5209)) {
u8 mask = 0, val = 0;
if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
if (chip->ocp_stat & (MS_OC_NOW | MS_OC_EVER)) {
mask |= MS_OCP_INT_CLR | MS_OC_CLR;
val |= MS_OCP_INT_CLR | MS_OC_CLR;
}
}
if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
mask |= SD_OCP_INT_CLR | SD_OC_CLR;
val |= SD_OCP_INT_CLR | SD_OC_CLR;
}
if (mask)
rtsx_write_register(chip, OCPCTL, mask, val);
} else {
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)
{
struct sd_info *sd_card = &(chip->sd_card);
struct ms_info *ms_card = &(chip->ms_card);
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;
if (CHECK_PID(chip, 0x5209)) {
min_N = 80;
max_N = 208;
max_div = CLK_DIV_8;
} else {
min_N = 60;
max_N = 120;
max_div = CLK_DIV_4;
}
if (CHECK_PID(chip, 0x5209) && (chip->cur_card == SD_CARD)) {
struct sd_info *sd_card = &(chip->sd_card);
if (CHK_SD30_SPEED(sd_card) || CHK_MMC_DDR52(sd_card))
sd_vpclk_phase_reset = 1;
}
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 (CHECK_PID(chip, 0x5209)) {
if (mcu_cnt > 15)
mcu_cnt = 15;
} else {
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) {
if (CHECK_PID(chip, 0x5209)) {
if (chip->cur_card == SD_CARD) {
if (CHK_SD_SDR104(sd_card)) {
ssc_depth = chip->ssc_depth_sd_sdr104;
} else if (CHK_SD_SDR50(sd_card)) {
ssc_depth = chip->ssc_depth_sd_sdr50;
} else if (CHK_SD_DDR50(sd_card)) {
ssc_depth = double_depth(chip->ssc_depth_sd_ddr50);
} else if (CHK_SD_HS(sd_card)) {
ssc_depth = double_depth(chip->ssc_depth_sd_hs);
} else if (CHK_MMC_52M(sd_card) || CHK_MMC_DDR52(sd_card)) {
ssc_depth = double_depth(chip->ssc_depth_mmc_52m);
} else {
ssc_depth = double_depth(chip->ssc_depth_low_speed);
}
} else if (chip->cur_card == MS_CARD) {
if (CHK_MSPRO(ms_card)) {
if (CHK_HG8BIT(ms_card)) {
ssc_depth = double_depth(chip->ssc_depth_ms_hg);
} else {
ssc_depth = double_depth(chip->ssc_depth_ms_4bit);
}
} else {
if (CHK_MS4BIT(ms_card)) {
ssc_depth = double_depth(chip->ssc_depth_ms_4bit);
} else {
ssc_depth = double_depth(chip->ssc_depth_low_speed);
}
}
} else {
ssc_depth = double_depth(chip->ssc_depth_low_speed);
}
if (ssc_depth) {
if (div == CLK_DIV_2) {
if (ssc_depth > 1) {
ssc_depth -= 1;
} else {
ssc_depth = SSC_DEPTH_4M;
}
} else if (div == CLK_DIV_4) {
if (ssc_depth > 2) {
ssc_depth -= 2;
} else {
ssc_depth = SSC_DEPTH_4M;
}
} else if (div == CLK_DIV_8) {
if (ssc_depth > 3) {
ssc_depth -= 3;
} else {
ssc_depth = SSC_DEPTH_4M;
}
}
}
} else {
ssc_depth = 0x01;
N -= 2;
}
} else {
ssc_depth = 0;
}
if (CHECK_PID(chip, 0x5209)) {
ssc_depth_mask = SSC_DEPTH_MASK;
} else {
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;
if (CHECK_PID(chip, 0x5209) && (chip->cur_card == SD_CARD)) {
struct sd_info *sd_card = &(chip->sd_card);
if (CHK_SD30_SPEED(sd_card) || CHK_MMC_DDR52(sd_card))
sd_vpclk_phase_reset = 1;
}
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);
if (CHECK_PID(chip, 0x5209) && (card == SD_CARD)) {
rtsx_add_cmd(chip, WRITE_REG_CMD, PWR_GATE_CTRL, LDO3318_PWR_MASK, LDO_SUSPEND);
}
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);
if (CHECK_PID(chip, 0x5209) && (card == SD_CARD)) {
rtsx_add_cmd(chip, WRITE_REG_CMD, PWR_GATE_CTRL, LDO3318_PWR_MASK, LDO_ON);
}
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;
}
if (CHECK_PID(chip, 0x5209)) {
mask |= PMOS_STRG_MASK;
val |= PMOS_STRG_400mA;
}
RTSX_WRITE_REG(chip, CARD_PWR_CTL, mask, val);
if (CHECK_PID(chip, 0x5209) && (card == SD_CARD)) {
RTSX_WRITE_REG(chip, PWR_GATE_CTRL, LDO3318_PWR_MASK, LDO_OFF);
}
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, 0x5209) || 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;
}
}
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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 SDIO_CFG 0xFE70 /* RTS5209 */
#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 OCPGLITCH 0xFC17 /* 5209 */
#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 */
/* Driver for Realtek PCI-Express card reader
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/workqueue.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
if (CHECK_PID(chip, 0x5209)) {
if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
reg |= MS_OC_INT_EN | SD_OC_INT_EN;
} else {
reg |= SD_OC_INT_EN;
}
} else {
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 (CHECK_PID(chip, 0x5209)) {
RTSX_READ_REG(chip, SDIO_CFG, &tmp);
if (tmp & SDIO_BUS_AUTO_SWITCH)
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);
if (CHECK_PID(chip, 0x5209)) {
cd_toggle_mask = 0x10;
} else {
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);
} else {
RTSX_WRITE_REG(chip, SDIO_CFG, SDIO_BUS_AUTO_SWITCH, 0);
}
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);
} else {
RTSX_WRITE_REG(chip, SDIO_CFG,
SDIO_BUS_AUTO_SWITCH, SDIO_BUS_AUTO_SWITCH);
}
chip->chip_insert_with_sdio = 1;
chip->sd_io = 1;
}
} else {
if (CHECK_PID(chip, 0x5209)) {
RTSX_WRITE_REG(chip, TLPTISTAT, 0x10, 0x10);
} 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);
if (CHECK_PID(chip, 0x5209) && chip->asic_code) {
u16 val;
/* optimize PHY */
retval = rtsx_write_phy_register(chip, 0x00, 0xB966);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
retval = rtsx_write_phy_register(chip, 0x01, 0x713F);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
retval = rtsx_write_phy_register(chip, 0x03, 0xA549);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
retval = rtsx_write_phy_register(chip, 0x06, 0xB235);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
retval = rtsx_write_phy_register(chip, 0x07, 0xEF40);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
retval = rtsx_write_phy_register(chip, 0x1E, 0xF8EB);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
retval = rtsx_write_phy_register(chip, 0x19, 0xFE6C);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
wait_timeout(1);
retval = rtsx_write_phy_register(chip, 0x0A, 0x05C0);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
retval = rtsx_write_cfg_dw(chip, 1, 0x110, 0xFFFF, 0xFFFF);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
retval = rtsx_read_phy_register(chip, 0x08, &val);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
RTSX_DEBUGP("Read from phy 0x08: 0x%04x\n", val);
if (chip->phy_voltage) {
chip->phy_voltage &= 0x3F;
RTSX_DEBUGP("chip->phy_voltage = 0x%x\n", chip->phy_voltage);
val &= ~0x3F;
val |= chip->phy_voltage;
RTSX_DEBUGP("Write to phy 0x08: 0x%04x\n", val);
retval = rtsx_write_phy_register(chip, 0x08, val);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
} else {
chip->phy_voltage = (u8)(val & 0x3F);
RTSX_DEBUGP("Default, chip->phy_voltage = 0x%x\n", chip->phy_voltage);
}
}
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);
}
if (CHECK_PID(chip, 0x5209)) {
RTSX_WRITE_REG(chip, OCPPARA1, SD_OCP_TIME_MASK | MS_OCP_TIME_MASK,
SD_OCP_TIME_800 | MS_OCP_TIME_800);
RTSX_WRITE_REG(chip, OCPPARA2, SD_OCP_THD_MASK | MS_OCP_THD_MASK,
chip->sd_400mA_ocp_thd | (chip->ms_ocp_thd << 4));
if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
RTSX_WRITE_REG(chip, OCPGLITCH, SD_OCP_GLITCH_MASK | MS_OCP_GLITCH_MASK,
SD_OCP_GLITCH_10000 | MS_OCP_GLITCH_10000);
} else {
RTSX_WRITE_REG(chip, OCPGLITCH, SD_OCP_GLITCH_MASK, SD_OCP_GLITCH_10000);
}
RTSX_WRITE_REG(chip, OCPCTL, 0xFF,
SD_OCP_INT_EN | SD_DETECT_EN | MS_OCP_INT_EN | MS_DETECT_EN);
} else {
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);
if (CHECK_PID(chip, 0x5209)) {
RTSX_WRITE_REG(chip, SD30_DRIVE_SEL, 0x07, chip->sd30_drive_sel_3v3);
}
#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, 0x5209)) {
retval = rtsx_write_cfg_dw(chip, 1, 0xC0, 0xFF, chip->aspm_l0s_l1_en);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
} else 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, 0x5209)) {
retval = rtsx_write_cfg_dw(chip, 0, 0x70C, 0xFF000000, 0x5B);
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);
if (CHECK_PID(chip, 0x5209)) {
RTSX_WRITE_REG(chip, PWD_SUSPEND_EN, 0xFF, 0xFF);
RTSX_WRITE_REG(chip, PWR_GATE_CTRL, PWR_GATE_EN, PWR_GATE_EN);
}
/* 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);
}
#ifdef HW_INT_WRITE_CLR
if (CHECK_PID(chip, 0x5209)) {
/* Set interrupt write clear */
RTSX_WRITE_REG(chip, NFTS_TX_CTRL, 0x02, 0);
}
#endif
chip->need_reset = 0;
chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
#ifdef HW_INT_WRITE_CLR
if (CHECK_PID(chip, 0x5209)) {
/* Clear interrupt flag */
rtsx_writel(chip, RTSX_BIPR, chip->int_reg);
}
#endif
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);
} else if (CHECK_PID(chip, 0x5209)) {
if (chip->force_clkreq_0) {
RTSX_WRITE_REG(chip, PETXCFG, 0x08, 0x08);
} else {
RTSX_WRITE_REG(chip, PETXCFG, 0x08, 0x00);
}
}
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;
}
int rts5209_init(struct rtsx_chip *chip)
{
int retval;
u32 lval = 0;
u8 val = 0;
val = rtsx_readb(chip, 0x1C);
if ((val & 0x10) == 0) {
chip->asic_code = 1;
} else {
chip->asic_code = 0;
}
chip->ic_version = val & 0x0F;
chip->phy_debug_mode = 0;
chip->aux_pwr_exist = 0;
chip->ms_power_class_en = 0x03;
retval = rtsx_read_cfg_dw(chip, 0, 0x724, &lval);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
RTSX_DEBUGP("dw in 0x724: 0x%x\n", lval);
val = (u8)lval;
if (!(val & 0x80)) {
if (val & 0x04) {
SET_SDIO_EXIST(chip);
} else {
CLR_SDIO_EXIST(chip);
}
if (val & 0x02) {
chip->hw_bypass_sd = 0;
} else {
chip->hw_bypass_sd = 1;
}
} else {
SET_SDIO_EXIST(chip);
chip->hw_bypass_sd = 0;
}
if (chip->use_hw_setting) {
u8 clk;
chip->aspm_l0s_l1_en = (val >> 5) & 0x03;
if (val & 0x08) {
chip->lun_mode = DEFAULT_SINGLE;
} else {
chip->lun_mode = SD_MS_2LUN;
}
val = (u8)(lval >> 8);
clk = (val >> 5) & 0x07;
if (clk != 0x07) {
chip->asic_sd_sdr50_clk = 98 - clk * 2;
}
if (val & 0x10) {
chip->auto_delink_en = 1;
} else {
chip->auto_delink_en = 0;
}
if (chip->ss_en == 2) {
chip->ss_en = 0;
} else {
if (val & 0x08) {
chip->ss_en = 1;
} else {
chip->ss_en = 0;
}
}
clk = val & 0x07;
if (clk != 0x07)
chip->asic_ms_hg_clk = (59 - clk) * 2;
val = (u8)(lval >> 16);
clk = (val >> 6) & 0x03;
if (clk != 0x03) {
chip->asic_sd_hs_clk = (49 - clk * 2) * 2;
chip->asic_mmc_52m_clk = (49 - clk * 2) * 2;
}
clk = (val >> 4) & 0x03;
if (clk != 0x03)
chip->asic_sd_ddr50_clk = (48 - clk * 2) * 2;
if (val & 0x01) {
chip->sdr104_en = 1;
} else {
chip->sdr104_en = 0;
}
if (val & 0x02) {
chip->ddr50_en = 1;
} else {
chip->ddr50_en = 0;
}
if (val & 0x04) {
chip->sdr50_en = 1;
} else {
chip->sdr50_en = 0;
}
val = (u8)(lval >> 24);
clk = (val >> 5) & 0x07;
if (clk != 0x07)
chip->asic_sd_sdr104_clk = 206 - clk * 3;
if (val & 0x10) {
chip->power_down_in_ss = 1;
} else {
chip->power_down_in_ss = 0;
}
chip->ms_power_class_en = val & 0x03;
}
if (chip->hp_watch_bios_hotplug && chip->auto_delink_en) {
u8 reg58, reg5b;
retval = rtsx_read_pci_cfg_byte(0x00,
0x1C, 0x02, 0x58, &reg58);
if (retval < 0) {
return STATUS_SUCCESS;
}
retval = rtsx_read_pci_cfg_byte(0x00,
0x1C, 0x02, 0x5B, &reg5b);
if (retval < 0) {
return STATUS_SUCCESS;
}
RTSX_DEBUGP("reg58 = 0x%x, reg5b = 0x%x\n", reg58, reg5b);
if ((reg58 == 0x00) && (reg5b == 0x01)) {
chip->auto_delink_en = 0;
}
}
return STATUS_SUCCESS;
}
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;
}
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, 0x5209)) {
retval = rts5209_init(chip);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, STATUS_FAIL);
}
} else 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
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 (CHECK_PID(chip, 0x5209) &&
CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
if (chip->ocp_int & SD_OC_INT)
sd_power_off_card3v3(chip);
if (chip->ocp_int & MS_OC_INT)
ms_power_off_card3v3(chip);
} else {
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;
if (CHECK_PID(chip, 0x5209)) {
rtsx_read_register(chip, SD_BUS_STAT, &val);
if (val & SD_DAT0_STATUS) {
sd_card->sd_erase_status = SD_NOT_ERASE;
sd_card->sd_lock_notify = 1;
chip->need_reinit |= SD_CARD;
}
} else {
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)) {
#if CONFIG_RTS_PSTOR_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);
}
#ifdef HW_INT_WRITE_CLR
if (CHECK_PID(chip, 0x5209)) {
rtsx_writel(chip, RTSX_BIPR, 0xFFFFFFFF);
RTSX_DEBUGP("RTSX_BIPR: 0x%x\n", rtsx_readl(chip, RTSX_BIPR));
}
#endif
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 = (u32 *)vmalloc(dw_len * 4);
if (!data) {
TRACE_RET(chip, STATUS_NOMEM);
}
memset(data, 0, dw_len * 4);
mask = (u32 *)vmalloc(dw_len * 4);
if (!mask) {
vfree(data);
TRACE_RET(chip, STATUS_NOMEM);
}
memset(mask, 0, dw_len * 4);
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 = (u32 *)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;
if (CHECK_PID(chip, 0x5209)) {
if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
ocp_int = MS_OC_INT | SD_OC_INT;
} else {
ocp_int = SD_OC_INT;
}
} else {
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);
#ifdef HW_INT_WRITE_CLR
if (CHECK_PID(chip, 0x5209)) {
rtsx_writel(chip, RTSX_BIPR, chip->int_reg);
}
#endif
if (((chip->int_reg & int_enable) == 0) || (chip->int_reg == 0xFFFFFFFF))
return STATUS_FAIL;
if (!chip->msi_en) {
if (CHECK_PID(chip, 0x5209)) {
u8 val;
rtsx_read_config_byte(chip, 0x05, &val);
if (val & 0x04) {
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);
} else if (CHECK_PID(chip, 0x5209)) {
rtsx_write_register(chip, TLPTISTAT, 0x10, 0x10);
/* Enable sdio_bus_auto_switch */
rtsx_write_register(chip, SDIO_CFG, SDIO_BUS_AUTO_SWITCH, SDIO_BUS_AUTO_SWITCH);
}
}
#endif
if (CHECK_PID(chip, 0x5208) && (chip->ic_version >= IC_VER_D)) {
/* u_force_clkreq_0 */
rtsx_write_register(chip, PETXCFG, 0x08, 0x08);
} else if (CHECK_PID(chip, 0x5209)) {
/* 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;
}
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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 Infomation ----*/
#define SNSKEYINFO_LEN 3 /* length of sense key infomation */
#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]; /* infomation */
unsigned char ad_sense_len; /* additional sense data length */
unsigned char cmd_info[4]; /* command specific infomation */
unsigned char asc; /* ASC */
unsigned char ascq; /* ASCQ */
unsigned char rfu; /* FRU */
unsigned char sns_key_info[3]; /* sense key specific infomation */
};
/* 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 */
/* Driver for Realtek PCI-Express card reader
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/sched.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;
}
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(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);
}
if (CHECK_PID(chip, 0x5209) && chip->max_payload) {
u8 val = 0x10 | (chip->max_payload << 5);
retval = rtsx_write_cfg_dw(chip, 0, 0x78, 0xFF, val);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, TRANSPORT_ERROR);
}
}
}
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:
if (srb->sc_data_direction == DMA_TO_DEVICE) {
if (CHECK_PID(chip, 0x5209) && chip->max_payload) {
retval = rtsx_write_cfg_dw(chip, 0, 0x78, 0xFF, 0x10);
if (retval != STATUS_SUCCESS) {
TRACE_RET(chip, TRANSPORT_ERROR);
}
}
}
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(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 = (u8 *)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(scsi_bufflen(srb), (unsigned int)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(scsi_bufflen(srb), (unsigned int)len);
buf = (u8 *)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 = (u8 *)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(scsi_bufflen(srb), (unsigned int)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(scsi_bufflen(srb), (unsigned int)len);
buf = (u8 *)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 = (unsigned char *)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(scsi_bufflen(srb), (unsigned int)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(scsi_bufflen(srb), (unsigned int)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(len, (u16)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(scsi_bufflen(srb), (unsigned int)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 = (u8 *)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(scsi_bufflen(srb), (unsigned int)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(scsi_bufflen(srb), (unsigned int)len);
buf = (u8 *)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 = (u8 *)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(scsi_bufflen(srb), (unsigned int)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(scsi_bufflen(srb), (unsigned int)len);
buf = (u8 *)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 = (u8 *)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(scsi_bufflen(srb), (unsigned int)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(scsi_bufflen(srb), (unsigned int)len);
buf = (u8 *)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 = (u8 *)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(scsi_bufflen(srb), (unsigned int)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(scsi_bufflen(srb), (unsigned int)len);
buf = (u8 *)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(scsi_bufflen(srb), (unsigned int)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
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 = (u8 *)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
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;
}
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;
}
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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 Extention */
#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
#define SENSE_TYPE_MEDIA_READ_FORBIDDEN 0x10 /* FOR Locked SD card*/
#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 */
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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 */
/* Driver for Realtek PCI-Express card reader
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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);
}
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;
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;
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 = sg_next(sg);
for (i = *index; i < sg_cnt; i++) {
dma_addr_t addr;
unsigned int len;
u8 option;
addr = sg_dma_address(sg);
len = sg_dma_len(sg);
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 = sg_next(sg);
}
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;
}
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;
}
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_sglist(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;
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);
for (i = 0; i < buf_cnt; i++) {
u32 bier = 0;
u32 val = (1 << 31);
dma_addr_t addr = sg_dma_address(sg + i);
unsigned int len = sg_dma_len(sg + i);
RTSX_DEBUGP("dma_addr = 0x%x, dma_len = %d\n",
(unsigned int)addr, len);
val |= (u32)(dir & 0x01) << 29;
val |= (u32)(len & 0x00FFFFFF);
spin_lock_irq(&rtsx->reg_lock);
init_completion(&trans_done);
if (i == (buf_cnt - 1)) {
/* If last transfer, disable data interrupt */
bier = rtsx_readl(chip, RTSX_BIER);
rtsx_writel(chip, RTSX_BIER, bier & 0xBFFFFFFF);
}
rtsx_writel(chip, RTSX_HDBAR, 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;
if (i == (buf_cnt - 1))
rtsx_writel(chip, RTSX_BIER, bier);
goto out;
}
spin_lock_irq(&rtsx->reg_lock);
if (rtsx->trans_result == TRANS_RESULT_FAIL) {
err = -EIO;
spin_unlock_irq(&rtsx->reg_lock);
if (i == (buf_cnt - 1))
rtsx_writel(chip, RTSX_BIER, bier);
goto out;
}
spin_unlock_irq(&rtsx->reg_lock);
if (i == (buf_cnt - 1)) {
/* If last transfer, enable data interrupt
* after transfer finished
*/
rtsx_writel(chip, RTSX_BIER, bier);
}
}
/* 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;
}
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;
}
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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 */
This source diff could not be displayed because it is too large. You can view the blob instead.
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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
/* 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 */
/* Driver for Realtek PCI-Express card reader
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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;
}
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((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 = (u8 *)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 = (u8 *)rtsx_alloc_dma_buf(chip, 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) {
rtsx_free_dma_buf(chip, 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);
rtsx_free_dma_buf(chip, 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 = rtsx_alloc_dma_buf(chip, 4, GFP_KERNEL);
if (!buf) {
TRACE_RET(chip, STATUS_ERROR);
}
while (len) {
retval = sf_enable_write(chip, SPI_WREN);
if (retval != STATUS_SUCCESS) {
rtsx_free_dma_buf(chip, 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) {
rtsx_free_dma_buf(chip, 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) {
rtsx_free_dma_buf(chip, buf);
TRACE_RET(chip, STATUS_FAIL);
}
addr++;
len--;
}
rtsx_free_dma_buf(chip, 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 = rtsx_alloc_dma_buf(chip, 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) {
rtsx_free_dma_buf(chip, 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) {
rtsx_free_dma_buf(chip, buf);
TRACE_RET(chip, STATUS_FAIL);
}
len--;
}
rtsx_free_dma_buf(chip, 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 = rtsx_alloc_dma_buf(chip, 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) {
rtsx_free_dma_buf(chip, 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) {
rtsx_free_dma_buf(chip, 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) {
rtsx_free_dma_buf(chip, buf);
TRACE_RET(chip, STATUS_FAIL);
}
addr += pagelen;
len -= pagelen;
}
rtsx_free_dma_buf(chip, 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;
}
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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 */
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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
#if CONFIG_RTS_PSTOR_DEBUG
static inline void rtsx_dump(u8 *buf, int buf_len)
{
int i;
u8 tmp[16] = {0};
u8 *_ptr = buf;
for (i = 0; i < ((buf_len)/16); i++) {
RTSX_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x %02x %02x\n",
_ptr[0], _ptr[1], _ptr[2], _ptr[3], _ptr[4], _ptr[5],
_ptr[6], _ptr[7], _ptr[8], _ptr[9], _ptr[10], _ptr[11],
_ptr[12], _ptr[13], _ptr[14], _ptr[15]);
_ptr += 16;
}
if ((buf_len) % 16) {
memcpy(tmp, _ptr, (buf_len) % 16);
_ptr = tmp;
RTSX_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x %02x %02x\n",
_ptr[0], _ptr[1], _ptr[2], _ptr[3], _ptr[4], _ptr[5],
_ptr[6], _ptr[7], _ptr[8], _ptr[9], _ptr[10], _ptr[11],
_ptr[12], _ptr[13], _ptr[14], _ptr[15]);
}
}
#define RTSX_DUMP(buf, buf_len) rtsx_dump((u8 *)(buf), (buf_len))
#else
#define RTSX_DUMP(buf, buf_len)
#endif
#endif /* __REALTEK_RTSX_TRACE_H */
/* Driver for Realtek PCI-Express card reader
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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 "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, 0x5209)) {
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, 0xD5);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x15);
} else 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, 0x5209)) {
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0xAA);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0xD5);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x15);
} else 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, 0x5209)) {
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, 0xD5);
RTSX_WRITE_REG(chip, CARD_PULL_CTL4, 0xFF, 0x55);
RTSX_WRITE_REG(chip, CARD_PULL_CTL5, 0xFF, 0x55);
RTSX_WRITE_REG(chip, CARD_PULL_CTL6, 0xFF, 0x15);
} else 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 void xd_clear_dma_buffer(struct rtsx_chip *chip)
{
if (CHECK_PID(chip, 0x5209)) {
int retval;
u8 *buf;
RTSX_DEBUGP("xD ECC error, dummy write!\n");
buf = (u8 *)rtsx_alloc_dma_buf(chip, 512, GFP_KERNEL);
if (!buf) {
return;
}
rtsx_init_cmd(chip);
trans_dma_enable(DMA_TO_DEVICE, chip, 512, DMA_512);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_CLK_EN, SD_CLK_EN, SD_CLK_EN);
if (chip->asic_code) {
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0xAA);
} else {
rtsx_add_cmd(chip, WRITE_REG_CMD, FPGA_PULL_CTL,
FPGA_SD_PULL_CTL_BIT, 0);
}
rtsx_add_cmd(chip, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00);
rtsx_add_cmd(chip, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02);
rtsx_add_cmd(chip, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1);
rtsx_add_cmd(chip, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0);
rtsx_add_cmd(chip, WRITE_REG_CMD, SD_CFG1, 0x03, SD_BUS_WIDTH_4);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
rtsx_add_cmd(chip, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START);
rtsx_add_cmd(chip, CHECK_REG_CMD, SD_TRANSFER, SD_TRANSFER_END, SD_TRANSFER_END);
rtsx_send_cmd_no_wait(chip);
retval = rtsx_transfer_data(chip, SD_CARD, buf, 512, 0, DMA_TO_DEVICE, 100);
if (retval < 0) {
u8 val;
rtsx_read_register(chip, SD_STAT1, &val);
RTSX_DEBUGP("SD_STAT1: 0x%x\n", val);
rtsx_read_register(chip, SD_STAT2, &val);
RTSX_DEBUGP("SD_STAT2: 0x%x\n", val);
rtsx_read_register(chip, SD_BUS_STAT, &val);
RTSX_DEBUGP("SD_BUS_STAT: 0x%x\n", val);
rtsx_write_register(chip, CARD_STOP, SD_STOP | SD_CLR_ERR, SD_STOP | SD_CLR_ERR);
}
rtsx_free_dma_buf(chip, buf);
if (chip->asic_code) {
rtsx_write_register(chip, CARD_PULL_CTL2, 0xFF, 0x55);
} else {
rtsx_write_register(chip, FPGA_PULL_CTL,
FPGA_SD_PULL_CTL_BIT, FPGA_SD_PULL_CTL_BIT);
}
rtsx_write_register(chip, CARD_SELECT, 0x07, XD_MOD_SEL);
rtsx_write_register(chip, CARD_CLK_EN, SD_CLK_EN, 0);
}
}
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);
if (CHECK_PID(chip, 0x5209)) {
RTSX_WRITE_REG(chip, CARD_PULL_CTL1, 0xFF, 0xAA);
}
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 = (struct zone_entry *)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, 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 = (u16 *)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 = (u16 *)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);
xd_clear_dma_buffer(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;
}
/* Driver for Realtek PCI-Express card reader
* Header file
*
* Copyright(c) 2009 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:
* wwang (wei_wang@realsil.com.cn)
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
#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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment