// SPDX-License-Identifier: GPL-2.0+
|
/*
|
* Driver for Realtek PCI-Express card reader
|
*
|
* Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
|
*
|
* Author:
|
* Wei WANG (wei_wang@realsil.com.cn)
|
* Micky Ching (micky_ching@realsil.com.cn)
|
*/
|
|
#include <linux/blkdev.h>
|
#include <linux/kthread.h>
|
#include <linux/sched.h>
|
#include <linux/vmalloc.h>
|
|
#include "rtsx.h"
|
#include "sd.h"
|
#include "ms.h"
|
#include "spi.h"
|
|
void scsi_show_command(struct rtsx_chip *chip)
|
{
|
struct scsi_cmnd *srb = chip->srb;
|
char *what = NULL;
|
bool unknown_cmd = false;
|
int len;
|
|
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 = true;
|
break;
|
}
|
|
if (srb->cmnd[0] != TEST_UNIT_READY)
|
dev_dbg(rtsx_dev(chip), "Command %s (%d bytes)\n",
|
what, srb->cmd_len);
|
|
if (unknown_cmd) {
|
len = min_t(unsigned short, srb->cmd_len, 16);
|
dev_dbg(rtsx_dev(chip), "%*ph\n", len, srb->cmnd);
|
}
|
}
|
|
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) >> 4;
|
sense->sns_key_info[2] = sns_key_info1 & 0x0f;
|
}
|
}
|
|
static int test_unit_ready(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned int lun = SCSI_LUN(srb);
|
|
if (!check_card_ready(chip, lun)) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
|
return TRANSPORT_FAILED;
|
}
|
|
if (!(CHK_BIT(chip->lun_mc, lun))) {
|
SET_BIT(chip->lun_mc, lun);
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
|
return TRANSPORT_FAILED;
|
}
|
|
#ifdef SUPPORT_SD_LOCK
|
if (get_lun_card(chip, SCSI_LUN(srb)) == SD_CARD) {
|
struct sd_info *sd_card = &chip->sd_card;
|
|
if (sd_card->sd_lock_notify) {
|
sd_card->sd_lock_notify = 0;
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
|
return TRANSPORT_FAILED;
|
} else if (sd_card->sd_lock_status & SD_LOCKED) {
|
set_sense_type(chip, lun,
|
SENSE_TYPE_MEDIA_READ_FORBIDDEN);
|
return TRANSPORT_FAILED;
|
}
|
}
|
#endif
|
|
return TRANSPORT_GOOD;
|
}
|
|
static unsigned char formatter_inquiry_str[20] = {
|
'M', 'E', 'M', 'O', 'R', 'Y', 'S', 'T', 'I', 'C', 'K',
|
#ifdef SUPPORT_MAGIC_GATE
|
'-', 'M', 'G', /* Byte[47:49] */
|
#else
|
0x20, 0x20, 0x20, /* Byte[47:49] */
|
#endif
|
|
#ifdef SUPPORT_MAGIC_GATE
|
0x0B, /* Byte[50]: MG, MS, MSPro, MSXC */
|
#else
|
0x09, /* Byte[50]: MS, MSPro, MSXC */
|
#endif
|
0x00, /* Byte[51]: Category Specific Commands */
|
0x00, /* Byte[52]: Access Control and feature */
|
0x20, 0x20, 0x20, /* Byte[53:55] */
|
};
|
|
static int inquiry(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned int lun = SCSI_LUN(srb);
|
char *inquiry_default = (char *)"Generic-xD/SD/M.S. 1.00 ";
|
char *inquiry_sdms = (char *)"Generic-SD/MemoryStick 1.00 ";
|
char *inquiry_sd = (char *)"Generic-SD/MMC 1.00 ";
|
char *inquiry_ms = (char *)"Generic-MemoryStick 1.00 ";
|
char *inquiry_string;
|
unsigned char sendbytes;
|
unsigned char *buf;
|
u8 card = get_lun_card(chip, lun);
|
bool pro_formatter_flag = false;
|
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)
|
return 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 = true;
|
|
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);
|
strncpy(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;
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
|
return TRANSPORT_FAILED;
|
|
break;
|
}
|
|
return 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);
|
return 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)
|
return 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;
|
bool support_format = false;
|
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 = true;
|
buf[i++] = 0x40;
|
} else if (CHK_MSPRO(ms_card)) {
|
support_format = true;
|
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 data_size;
|
int status;
|
bool pro_formatter_flag;
|
unsigned char page_code, *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));
|
return TRANSPORT_FAILED;
|
}
|
#endif
|
|
pro_formatter_flag = false;
|
data_size = 8;
|
#ifdef SUPPORT_MAGIC_GATE
|
if ((chip->lun2card[lun] & MS_CARD)) {
|
if (!card || (card == MS_CARD)) {
|
data_size = 108;
|
if (chip->mspro_formatter_enable)
|
pro_formatter_flag = true;
|
}
|
}
|
#else
|
if (card == MS_CARD) {
|
if (chip->mspro_formatter_enable) {
|
pro_formatter_flag = true;
|
data_size = 108;
|
}
|
}
|
#endif
|
|
buf = kmalloc(data_size, GFP_KERNEL);
|
if (!buf)
|
return TRANSPORT_ERROR;
|
|
page_code = srb->cmnd[2] & 0x3f;
|
|
if ((page_code == 0x3F) || (page_code == 0x1C) ||
|
(page_code == 0x00) ||
|
(pro_formatter_flag && (page_code == 0x20))) {
|
if (srb->cmnd[0] == MODE_SENSE) {
|
if ((page_code == 0x3F) || (page_code == 0x20)) {
|
ms_mode_sense(chip, srb->cmnd[0],
|
lun, buf, data_size);
|
} else {
|
data_size = 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 ((page_code == 0x3F) || (page_code == 0x20)) {
|
ms_mode_sense(chip, srb->cmnd[0],
|
lun, buf, data_size);
|
} else {
|
data_size = 8;
|
buf[0] = 0x00;
|
buf[1] = 0x06;
|
buf[2] = 0x00;
|
if (check_card_wp(chip, lun))
|
buf[3] = 0x80;
|
else
|
buf[3] = 0x00;
|
buf[4] = 0x00;
|
buf[5] = 0x00;
|
buf[6] = 0x00;
|
buf[7] = 0x00;
|
}
|
}
|
status = TRANSPORT_GOOD;
|
} else {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
scsi_set_resid(srb, scsi_bufflen(srb));
|
status = TRANSPORT_FAILED;
|
}
|
|
if (status == TRANSPORT_GOOD) {
|
unsigned int len = min_t(unsigned int, scsi_bufflen(srb),
|
data_size);
|
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);
|
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 (sd_card->sd_erase_status) {
|
/* Accessing to any card is forbidden
|
* until the erase procedure of SD is completed
|
*/
|
dev_dbg(rtsx_dev(chip), "SD card being erased!\n");
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_READ_FORBIDDEN);
|
return TRANSPORT_FAILED;
|
}
|
|
if (get_lun_card(chip, lun) == SD_CARD) {
|
if (sd_card->sd_lock_status & SD_LOCKED) {
|
dev_dbg(rtsx_dev(chip), "SD card locked!\n");
|
set_sense_type(chip, lun,
|
SENSE_TYPE_MEDIA_READ_FORBIDDEN);
|
return 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];
|
if (sec_cnt == 0)
|
sec_cnt = 256;
|
} 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
if (sec_cnt == 0) {
|
scsi_set_resid(srb, 0);
|
return TRANSPORT_GOOD;
|
}
|
|
if (chip->rw_fail_cnt[lun] == 3) {
|
dev_dbg(rtsx_dev(chip), "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);
|
|
return TRANSPORT_FAILED;
|
}
|
|
if (srb->sc_data_direction == DMA_TO_DEVICE) {
|
if (check_card_wp(chip, lun)) {
|
dev_dbg(rtsx_dev(chip), "Write protected card!\n");
|
set_sense_type(chip, lun,
|
SENSE_TYPE_MEDIA_WRITE_PROTECT);
|
return TRANSPORT_FAILED;
|
}
|
}
|
|
retval = card_rw(srb, chip, start_sec, sec_cnt);
|
if (retval != STATUS_SUCCESS) {
|
if (chip->need_release & chip->lun2card[lun]) {
|
chip->rw_fail_cnt[lun] = 0;
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
|
} else {
|
chip->rw_fail_cnt[lun]++;
|
if (srb->sc_data_direction == DMA_FROM_DEVICE)
|
set_sense_type
|
(chip, lun,
|
SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
|
else
|
set_sense_type(chip, lun,
|
SENSE_TYPE_MEDIA_WRITE_ERR);
|
}
|
retval = TRANSPORT_FAILED;
|
goto exit;
|
} else {
|
chip->rw_fail_cnt[lun] = 0;
|
retval = TRANSPORT_GOOD;
|
}
|
|
scsi_set_resid(srb, 0);
|
|
exit:
|
return retval;
|
}
|
|
static int read_format_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned char *buf;
|
unsigned int lun = SCSI_LUN(srb);
|
unsigned int buf_len;
|
u8 card = get_lun_card(chip, lun);
|
u32 card_size;
|
int desc_cnt;
|
int i = 0;
|
|
if (!check_card_ready(chip, lun)) {
|
if (!chip->mspro_formatter_enable) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
|
return TRANSPORT_FAILED;
|
}
|
}
|
|
buf_len = (scsi_bufflen(srb) > 12) ? 0x14 : 12;
|
|
buf = kmalloc(buf_len, GFP_KERNEL);
|
if (!buf)
|
return TRANSPORT_ERROR;
|
|
buf[i++] = 0;
|
buf[i++] = 0;
|
buf[i++] = 0;
|
|
/* Capacity List Length */
|
if ((buf_len > 12) && chip->mspro_formatter_enable &&
|
(chip->lun2card[lun] & MS_CARD) &&
|
(!card || (card == MS_CARD))) {
|
buf[i++] = 0x10;
|
desc_cnt = 2;
|
} else {
|
buf[i++] = 0x08;
|
desc_cnt = 1;
|
}
|
|
while (desc_cnt) {
|
if (check_card_ready(chip, lun)) {
|
card_size = get_card_size(chip, lun);
|
buf[i++] = (unsigned char)(card_size >> 24);
|
buf[i++] = (unsigned char)(card_size >> 16);
|
buf[i++] = (unsigned char)(card_size >> 8);
|
buf[i++] = (unsigned char)card_size;
|
|
if (desc_cnt == 2)
|
buf[i++] = 2;
|
else
|
buf[i++] = 0;
|
} else {
|
buf[i++] = 0xFF;
|
buf[i++] = 0xFF;
|
buf[i++] = 0xFF;
|
buf[i++] = 0xFF;
|
|
if (desc_cnt == 2)
|
buf[i++] = 3;
|
else
|
buf[i++] = 0;
|
}
|
|
buf[i++] = 0x00;
|
buf[i++] = 0x02;
|
buf[i++] = 0x00;
|
|
desc_cnt--;
|
}
|
|
buf_len = min_t(unsigned int, scsi_bufflen(srb), buf_len);
|
rtsx_stor_set_xfer_buf(buf, buf_len, srb);
|
kfree(buf);
|
|
scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int read_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned char *buf;
|
unsigned int lun = SCSI_LUN(srb);
|
u32 card_size;
|
|
if (!check_card_ready(chip, lun)) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
|
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;
|
}
|
|
buf = kmalloc(8, GFP_KERNEL);
|
if (!buf)
|
return TRANSPORT_ERROR;
|
|
card_size = get_card_size(chip, lun);
|
buf[0] = (unsigned char)((card_size - 1) >> 24);
|
buf[1] = (unsigned char)((card_size - 1) >> 16);
|
buf[2] = (unsigned char)((card_size - 1) >> 8);
|
buf[3] = (unsigned char)(card_size - 1);
|
|
buf[4] = 0x00;
|
buf[5] = 0x00;
|
buf[6] = 0x02;
|
buf[7] = 0x00;
|
|
rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
|
kfree(buf);
|
|
scsi_set_resid(srb, 0);
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int read_eeprom(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned short len, i;
|
int retval;
|
u8 *buf;
|
|
rtsx_disable_aspm(chip);
|
|
if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
|
rtsx_exit_ss(chip);
|
wait_timeout(100);
|
}
|
rtsx_set_stat(chip, RTSX_STAT_RUN);
|
|
len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
|
|
buf = vmalloc(len);
|
if (!buf)
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
}
|
|
len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
|
rtsx_stor_set_xfer_buf(buf, len, srb);
|
scsi_set_resid(srb, scsi_bufflen(srb) - len);
|
|
vfree(buf);
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int write_eeprom(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned short len, i;
|
int retval;
|
u8 *buf;
|
|
rtsx_disable_aspm(chip);
|
|
if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
|
rtsx_exit_ss(chip);
|
wait_timeout(100);
|
}
|
rtsx_set_stat(chip, RTSX_STAT_RUN);
|
|
len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
|
|
retval = rtsx_force_power_on(chip, SSC_PDCTL);
|
if (retval != STATUS_SUCCESS) {
|
set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
} else {
|
len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb),
|
len);
|
buf = vmalloc(len);
|
if (!buf)
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
buf = vmalloc(len);
|
if (!buf)
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
}
|
|
len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
|
rtsx_stor_set_xfer_buf(buf, len, srb);
|
scsi_set_resid(srb, scsi_bufflen(srb) - len);
|
|
vfree(buf);
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int write_mem(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned short addr, len, i;
|
int retval;
|
u8 *buf;
|
|
rtsx_disable_aspm(chip);
|
|
if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
|
rtsx_exit_ss(chip);
|
wait_timeout(100);
|
}
|
rtsx_set_stat(chip, RTSX_STAT_RUN);
|
|
addr = ((u16)srb->cmnd[2] << 8) | srb->cmnd[3];
|
len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
|
|
if (addr < 0xFC00) {
|
set_sense_type(chip, SCSI_LUN(srb),
|
SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return TRANSPORT_FAILED;
|
}
|
|
len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
|
buf = vmalloc(len);
|
if (!buf)
|
return 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);
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
if (get_lun_card(chip, lun) != SD_CARD) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
|
return 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;
|
}
|
|
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);
|
dev_dbg(rtsx_dev(chip), "Host register (0x%x): 0x%x\n", addr, val);
|
|
buf[0] = (u8)(val >> 24);
|
buf[1] = (u8)(val >> 16);
|
buf[2] = (u8)(val >> 8);
|
buf[3] = (u8)val;
|
|
len = min_t(unsigned int, scsi_bufflen(srb), 4);
|
rtsx_stor_set_xfer_buf(buf, len, srb);
|
scsi_set_resid(srb, scsi_bufflen(srb) - len);
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int write_host_reg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
u8 addr, buf[4];
|
u32 val;
|
unsigned int len;
|
|
rtsx_disable_aspm(chip);
|
|
if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
|
rtsx_exit_ss(chip);
|
wait_timeout(100);
|
}
|
rtsx_set_stat(chip, RTSX_STAT_RUN);
|
|
addr = srb->cmnd[4];
|
|
len = min_t(unsigned int, scsi_bufflen(srb), 4);
|
rtsx_stor_get_xfer_buf(buf, len, srb);
|
scsi_set_resid(srb, scsi_bufflen(srb) - len);
|
|
val = ((u32)buf[0] << 24) | ((u32)buf[1] << 16) | ((u32)buf[2]
|
<< 8) | buf[3];
|
|
rtsx_writel(chip, addr, val);
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int set_variable(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned int 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
turn_off_led(chip, LED_GPIO);
|
}
|
} else {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int dma_access_ring_buffer(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
int retval;
|
unsigned int lun = SCSI_LUN(srb);
|
u16 len;
|
|
rtsx_disable_aspm(chip);
|
|
if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
|
rtsx_exit_ss(chip);
|
wait_timeout(100);
|
}
|
rtsx_set_stat(chip, RTSX_STAT_RUN);
|
|
len = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
|
len = min_t(u16, len, scsi_bufflen(srb));
|
|
if (srb->sc_data_direction == DMA_FROM_DEVICE)
|
dev_dbg(rtsx_dev(chip), "Read from device\n");
|
else
|
dev_dbg(rtsx_dev(chip), "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);
|
|
return 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;
|
}
|
|
dev_dbg(rtsx_dev(chip), "status[0x17] = 0x%x\n", status[0x17]);
|
#endif
|
|
status[0x18] = 0x8A;
|
status[0x1A] = 0x28;
|
#ifdef SUPPORT_SD_LOCK
|
status[0x1F] = 0x01;
|
#endif
|
|
buf_len = min_t(unsigned int, scsi_bufflen(srb), sizeof(status));
|
rtsx_stor_set_xfer_buf(status, buf_len, srb);
|
scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int set_chip_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
int phy_debug_mode;
|
int retval;
|
u16 reg;
|
|
if (!CHECK_PID(chip, 0x5208)) {
|
set_sense_type(chip, SCSI_LUN(srb),
|
SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return 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)
|
return TRANSPORT_FAILED;
|
|
rtsx_disable_bus_int(chip);
|
|
retval = rtsx_read_phy_register(chip, 0x1C, ®);
|
if (retval != STATUS_SUCCESS)
|
return TRANSPORT_FAILED;
|
|
reg |= 0x0001;
|
retval = rtsx_write_phy_register(chip, 0x1C, reg);
|
if (retval != STATUS_SUCCESS)
|
return TRANSPORT_FAILED;
|
} else {
|
chip->phy_debug_mode = 0;
|
retval = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0x77);
|
if (retval != STATUS_SUCCESS)
|
return TRANSPORT_FAILED;
|
|
rtsx_enable_bus_int(chip);
|
|
retval = rtsx_read_phy_register(chip, 0x1C, ®);
|
if (retval != STATUS_SUCCESS)
|
return TRANSPORT_FAILED;
|
|
reg &= 0xFFFE;
|
retval = rtsx_write_phy_register(chip, 0x1C, reg);
|
if (retval != STATUS_SUCCESS)
|
return 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);
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
if (retval != STATUS_SUCCESS) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
|
return TRANSPORT_FAILED;
|
}
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int suit_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
switch (srb->cmnd[3]) {
|
case INIT_BATCHCMD:
|
case ADD_BATCHCMD:
|
case SEND_BATCHCMD:
|
case GET_BATCHRSP:
|
return rw_mem_cmd_buf(srb, chip);
|
default:
|
return TRANSPORT_ERROR;
|
}
|
}
|
|
static int read_phy_register(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned short addr, len, i;
|
int retval;
|
u8 *buf;
|
u16 val;
|
|
rtsx_disable_aspm(chip);
|
|
if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
|
rtsx_exit_ss(chip);
|
wait_timeout(100);
|
}
|
rtsx_set_stat(chip, RTSX_STAT_RUN);
|
|
addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
|
len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
|
|
if (len % 2)
|
len -= len % 2;
|
|
if (len) {
|
buf = vmalloc(len);
|
if (!buf)
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
buf[2 * i] = (u8)(val >> 8);
|
buf[2 * i + 1] = (u8)val;
|
}
|
|
len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb),
|
len);
|
rtsx_stor_set_xfer_buf(buf, len, srb);
|
scsi_set_resid(srb, scsi_bufflen(srb) - len);
|
|
vfree(buf);
|
}
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int write_phy_register(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned short addr, len, i;
|
int retval;
|
u8 *buf;
|
u16 val;
|
|
rtsx_disable_aspm(chip);
|
|
if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
|
rtsx_exit_ss(chip);
|
wait_timeout(100);
|
}
|
rtsx_set_stat(chip, RTSX_STAT_RUN);
|
|
addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
|
len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
|
|
if (len % 2)
|
len -= len % 2;
|
|
if (len) {
|
len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb),
|
len);
|
|
buf = vmalloc(len);
|
if (!buf)
|
return 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);
|
return 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);
|
return 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);
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
} else {
|
set_sense_type(chip, SCSI_LUN(srb),
|
SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return TRANSPORT_FAILED;
|
}
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int read_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned short addr, len, i;
|
int retval;
|
u8 *buf;
|
|
rtsx_disable_aspm(chip);
|
|
if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
|
rtsx_exit_ss(chip);
|
wait_timeout(100);
|
}
|
rtsx_set_stat(chip, RTSX_STAT_RUN);
|
|
addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
|
len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
|
|
buf = vmalloc(len);
|
if (!buf)
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
}
|
|
len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
|
rtsx_stor_set_xfer_buf(buf, len, srb);
|
scsi_set_resid(srb, scsi_bufflen(srb) - len);
|
|
vfree(buf);
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int write_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned short addr, len, i;
|
int retval;
|
u8 *buf;
|
|
rtsx_disable_aspm(chip);
|
|
if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
|
rtsx_exit_ss(chip);
|
wait_timeout(100);
|
}
|
rtsx_set_stat(chip, RTSX_STAT_RUN);
|
|
addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
|
len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
|
|
len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
|
buf = vmalloc(len);
|
if (!buf)
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
}
|
|
vfree(buf);
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int read_efuse(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
int retval;
|
u8 addr, len, i;
|
u8 *buf;
|
|
rtsx_disable_aspm(chip);
|
|
if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
|
rtsx_exit_ss(chip);
|
wait_timeout(100);
|
}
|
rtsx_set_stat(chip, RTSX_STAT_RUN);
|
|
addr = srb->cmnd[4];
|
len = srb->cmnd[5];
|
|
buf = vmalloc(len);
|
if (!buf)
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
}
|
|
len = (u8)min_t(unsigned int, scsi_bufflen(srb), len);
|
rtsx_stor_set_xfer_buf(buf, len, srb);
|
scsi_set_resid(srb, scsi_bufflen(srb) - len);
|
|
vfree(buf);
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int write_efuse(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
int retval, result = TRANSPORT_GOOD;
|
u16 val;
|
u8 addr, len, i;
|
u8 *buf;
|
|
rtsx_disable_aspm(chip);
|
|
if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
|
rtsx_exit_ss(chip);
|
wait_timeout(100);
|
}
|
rtsx_set_stat(chip, RTSX_STAT_RUN);
|
|
addr = srb->cmnd[4];
|
len = srb->cmnd[5];
|
|
len = (u8)min_t(unsigned int, scsi_bufflen(srb), len);
|
buf = vmalloc(len);
|
if (!buf)
|
return 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);
|
return TRANSPORT_ERROR;
|
}
|
|
if (chip->asic_code) {
|
retval = rtsx_read_phy_register(chip, 0x08, &val);
|
if (retval != STATUS_SUCCESS) {
|
vfree(buf);
|
return TRANSPORT_ERROR;
|
}
|
|
retval = rtsx_write_register(chip, PWR_GATE_CTRL,
|
LDO3318_PWR_MASK, LDO_OFF);
|
if (retval != STATUS_SUCCESS) {
|
vfree(buf);
|
return TRANSPORT_ERROR;
|
}
|
|
wait_timeout(600);
|
|
retval = rtsx_write_phy_register(chip, 0x08,
|
0x4C00 | chip->phy_voltage);
|
if (retval != STATUS_SUCCESS) {
|
vfree(buf);
|
return TRANSPORT_ERROR;
|
}
|
|
retval = rtsx_write_register(chip, PWR_GATE_CTRL,
|
LDO3318_PWR_MASK, LDO_ON);
|
if (retval != STATUS_SUCCESS) {
|
vfree(buf);
|
return TRANSPORT_ERROR;
|
}
|
|
wait_timeout(600);
|
}
|
|
retval = card_power_on(chip, SPI_CARD);
|
if (retval != STATUS_SUCCESS) {
|
vfree(buf);
|
return 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;
|
goto exit;
|
}
|
}
|
|
exit:
|
vfree(buf);
|
|
retval = card_power_off(chip, SPI_CARD);
|
if (retval != STATUS_SUCCESS)
|
return TRANSPORT_ERROR;
|
|
if (chip->asic_code) {
|
retval = rtsx_write_register(chip, PWR_GATE_CTRL,
|
LDO3318_PWR_MASK, LDO_OFF);
|
if (retval != STATUS_SUCCESS)
|
return TRANSPORT_ERROR;
|
|
wait_timeout(600);
|
|
retval = rtsx_write_phy_register(chip, 0x08, val);
|
if (retval != STATUS_SUCCESS)
|
return TRANSPORT_ERROR;
|
|
retval = rtsx_write_register(chip, PWR_GATE_CTRL,
|
LDO3318_PWR_MASK, LDO_ON);
|
if (retval != STATUS_SUCCESS)
|
return TRANSPORT_ERROR;
|
}
|
|
return result;
|
}
|
|
static int read_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
int retval;
|
bool func_max;
|
u8 func;
|
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];
|
|
dev_dbg(rtsx_dev(chip), "%s: func = %d, addr = 0x%x, len = %d\n",
|
__func__, func, addr, len);
|
|
if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
|
func_max = true;
|
else
|
func_max = false;
|
|
if (func > func_max) {
|
set_sense_type(chip, SCSI_LUN(srb),
|
SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return TRANSPORT_FAILED;
|
}
|
|
buf = vmalloc(len);
|
if (!buf)
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
len = (u16)min_t(unsigned int, scsi_bufflen(srb), len);
|
rtsx_stor_set_xfer_buf(buf, len, srb);
|
scsi_set_resid(srb, scsi_bufflen(srb) - len);
|
|
vfree(buf);
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int write_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
int retval;
|
bool func_max;
|
u8 func;
|
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];
|
|
dev_dbg(rtsx_dev(chip), "%s: func = %d, addr = 0x%x\n",
|
__func__, func, addr);
|
|
if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
|
func_max = true;
|
else
|
func_max = false;
|
|
if (func > func_max) {
|
set_sense_type(chip, SCSI_LUN(srb),
|
SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return TRANSPORT_FAILED;
|
}
|
|
len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
|
buf = vmalloc(len);
|
if (!buf)
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
return result;
|
}
|
|
static int read_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
u8 rtsx_status[16];
|
int buf_len;
|
unsigned int lun = SCSI_LUN(srb);
|
|
rtsx_status[0] = (u8)(chip->vendor_id >> 8);
|
rtsx_status[1] = (u8)(chip->vendor_id);
|
|
rtsx_status[2] = (u8)(chip->product_id >> 8);
|
rtsx_status[3] = (u8)(chip->product_id);
|
|
rtsx_status[4] = (u8)lun;
|
|
if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
|
if (chip->lun2card[lun] == SD_CARD)
|
rtsx_status[5] = 2;
|
else
|
rtsx_status[5] = 3;
|
} else {
|
if (chip->card_exist) {
|
if (chip->card_exist & XD_CARD)
|
rtsx_status[5] = 4;
|
else if (chip->card_exist & SD_CARD)
|
rtsx_status[5] = 2;
|
else if (chip->card_exist & MS_CARD)
|
rtsx_status[5] = 3;
|
else
|
rtsx_status[5] = 7;
|
} else {
|
rtsx_status[5] = 7;
|
}
|
}
|
|
if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
|
rtsx_status[6] = 2;
|
else
|
rtsx_status[6] = 1;
|
|
rtsx_status[7] = (u8)(chip->product_id);
|
rtsx_status[8] = chip->ic_version;
|
|
if (check_card_exist(chip, lun))
|
rtsx_status[9] = 1;
|
else
|
rtsx_status[9] = 0;
|
|
if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
|
rtsx_status[10] = 0;
|
else
|
rtsx_status[10] = 1;
|
|
if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
|
if (chip->lun2card[lun] == SD_CARD)
|
rtsx_status[11] = SD_CARD;
|
else
|
rtsx_status[11] = MS_CARD;
|
} else {
|
rtsx_status[11] = XD_CARD | SD_CARD | MS_CARD;
|
}
|
|
if (check_card_ready(chip, lun))
|
rtsx_status[12] = 1;
|
else
|
rtsx_status[12] = 0;
|
|
if (get_lun_card(chip, lun) == XD_CARD) {
|
rtsx_status[13] = 0x40;
|
} else if (get_lun_card(chip, lun) == SD_CARD) {
|
struct sd_info *sd_card = &chip->sd_card;
|
|
rtsx_status[13] = 0x20;
|
if (CHK_SD(sd_card)) {
|
if (CHK_SD_HCXC(sd_card))
|
rtsx_status[13] |= 0x04;
|
if (CHK_SD_HS(sd_card))
|
rtsx_status[13] |= 0x02;
|
} else {
|
rtsx_status[13] |= 0x08;
|
if (CHK_MMC_52M(sd_card))
|
rtsx_status[13] |= 0x02;
|
if (CHK_MMC_SECTOR_MODE(sd_card))
|
rtsx_status[13] |= 0x04;
|
}
|
} else if (get_lun_card(chip, lun) == MS_CARD) {
|
struct ms_info *ms_card = &chip->ms_card;
|
|
if (CHK_MSPRO(ms_card)) {
|
rtsx_status[13] = 0x38;
|
if (CHK_HG8BIT(ms_card))
|
rtsx_status[13] |= 0x04;
|
#ifdef SUPPORT_MSXC
|
if (CHK_MSXC(ms_card))
|
rtsx_status[13] |= 0x01;
|
#endif
|
} else {
|
rtsx_status[13] = 0x30;
|
}
|
} else {
|
if (CHECK_LUN_MODE(chip, DEFAULT_SINGLE)) {
|
#ifdef SUPPORT_SDIO
|
if (chip->sd_io && chip->sd_int)
|
rtsx_status[13] = 0x60;
|
else
|
rtsx_status[13] = 0x70;
|
#else
|
rtsx_status[13] = 0x70;
|
#endif
|
} else {
|
if (chip->lun2card[lun] == SD_CARD)
|
rtsx_status[13] = 0x20;
|
else
|
rtsx_status[13] = 0x30;
|
}
|
}
|
|
rtsx_status[14] = 0x78;
|
if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
|
rtsx_status[15] = 0x83;
|
else
|
rtsx_status[15] = 0x82;
|
|
buf_len = min_t(unsigned int, scsi_bufflen(srb), sizeof(rtsx_status));
|
rtsx_stor_set_xfer_buf(rtsx_status, buf_len, srb);
|
scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);
|
|
return TRANSPORT_GOOD;
|
}
|
|
static int get_card_bus_width(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
unsigned int lun = SCSI_LUN(srb);
|
u8 card, bus_width;
|
|
if (!check_card_ready(chip, lun)) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
|
return 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);
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir);
|
|
if (result != STATUS_SUCCESS)
|
return 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;
|
|
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);
|
return 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];
|
if (sec_cnt == 0)
|
sec_cnt = 256;
|
} 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);
|
bool quick_format;
|
int retval;
|
|
if (get_lun_card(chip, lun) != MS_CARD) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
}
|
rtsx_set_stat(chip, RTSX_STAT_RUN);
|
|
if (srb->cmnd[8] & 0x01)
|
quick_format = false;
|
else
|
quick_format = true;
|
|
if (!(chip->card_ready & MS_CARD)) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
|
return TRANSPORT_FAILED;
|
}
|
|
if (chip->card_wp & MS_CARD) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT);
|
return TRANSPORT_FAILED;
|
}
|
|
if (!CHK_MSPRO(ms_card)) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
scsi_set_resid(srb, 0);
|
return TRANSPORT_GOOD;
|
}
|
|
#ifdef SUPPORT_PCGL_1P18
|
static int get_ms_information(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
struct ms_info *ms_card = &chip->ms_card;
|
unsigned int lun = SCSI_LUN(srb);
|
u8 dev_info_id, data_len;
|
u8 *buf;
|
unsigned int buf_len;
|
int i;
|
|
if (!check_card_ready(chip, lun)) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
|
return TRANSPORT_FAILED;
|
}
|
if (get_lun_card(chip, lun) != MS_CARD) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
|
return 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);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
if (dev_info_id == 0x15) {
|
buf_len = 0x3C;
|
data_len = 0x3A;
|
} else {
|
buf_len = 0x6C;
|
data_len = 0x6A;
|
}
|
|
buf = kmalloc(buf_len, GFP_KERNEL);
|
if (!buf)
|
return 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);
|
scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);
|
|
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_extension_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);
|
return TRANSPORT_FAILED;
|
}
|
if (get_lun_card(chip, lun) != SD_CARD) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
|
return 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);
|
return TRANSPORT_FAILED;
|
}
|
|
return result;
|
}
|
#endif
|
|
#ifdef SUPPORT_MAGIC_GATE
|
static int mg_report_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
struct ms_info *ms_card = &chip->ms_card;
|
unsigned int lun = SCSI_LUN(srb);
|
int retval;
|
u8 key_format;
|
|
rtsx_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);
|
return TRANSPORT_FAILED;
|
}
|
if (get_lun_card(chip, lun) != MS_CARD) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
|
return TRANSPORT_FAILED;
|
}
|
|
if (srb->cmnd[7] != KC_MG_R_PRO) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return TRANSPORT_FAILED;
|
}
|
|
if (!CHK_MSPRO(ms_card)) {
|
set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
|
return TRANSPORT_FAILED;
|
}
|
|
key_format = srb->cmnd[10] & 0x3F;
|
dev_dbg(rtsx_dev(chip), "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)
|
return TRANSPORT_FAILED;
|
|
} else {
|
set_sense_type(chip, lun,
|
SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return 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)
|
return TRANSPORT_FAILED;
|
|
} else {
|
set_sense_type(chip, lun,
|
SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return 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)
|
return TRANSPORT_FAILED;
|
|
} else {
|
set_sense_type(chip, lun,
|
SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return TRANSPORT_FAILED;
|
}
|
break;
|
|
default:
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return TRANSPORT_FAILED;
|
}
|
|
scsi_set_resid(srb, 0);
|
return TRANSPORT_GOOD;
|
}
|
|
static int mg_send_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
|
{
|
struct ms_info *ms_card = &chip->ms_card;
|
unsigned int lun = SCSI_LUN(srb);
|
int retval;
|
u8 key_format;
|
|
rtsx_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);
|
return TRANSPORT_FAILED;
|
}
|
if (check_card_wp(chip, lun)) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT);
|
return TRANSPORT_FAILED;
|
}
|
if (get_lun_card(chip, lun) != MS_CARD) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
|
return TRANSPORT_FAILED;
|
}
|
|
if (srb->cmnd[7] != KC_MG_R_PRO) {
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return TRANSPORT_FAILED;
|
}
|
|
if (!CHK_MSPRO(ms_card)) {
|
set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
|
return TRANSPORT_FAILED;
|
}
|
|
key_format = srb->cmnd[10] & 0x3F;
|
dev_dbg(rtsx_dev(chip), "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)
|
return TRANSPORT_FAILED;
|
|
} else {
|
set_sense_type(chip, lun,
|
SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return 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)
|
return TRANSPORT_FAILED;
|
|
} else {
|
set_sense_type(chip, lun,
|
SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return 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)
|
return TRANSPORT_FAILED;
|
|
} else {
|
set_sense_type(chip, lun,
|
SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return 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)
|
return TRANSPORT_FAILED;
|
|
} else {
|
set_sense_type(chip, lun,
|
SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return TRANSPORT_FAILED;
|
}
|
break;
|
|
default:
|
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
|
return 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);
|
return 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));
|
return 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_extension_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;
|
}
|