| /* Driver for Realtek PCI-Express card reader |
| * |
| * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2, or (at your option) any |
| * later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, see <http://www.gnu.org/licenses/>. |
| * |
| * Author: |
| * Wei WANG (wei_wang@realsil.com.cn) |
| * Micky Ching (micky_ching@realsil.com.cn) |
| */ |
| |
| #include <linux/blkdev.h> |
| #include <linux/kthread.h> |
| #include <linux/sched.h> |
| #include <linux/vmalloc.h> |
| |
| #include "rtsx.h" |
| #include "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) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| |
| break; |
| } |
| |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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) { |
| rtsx_trace(chip); |
| 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)); |
| rtsx_trace(chip); |
| 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) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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; |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| } |
| |
| buf_len = (scsi_bufflen(srb) > 12) ? 0x14 : 12; |
| |
| buf = kmalloc(buf_len, GFP_KERNEL); |
| if (!buf) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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) { |
| rtsx_trace(chip); |
| 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) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| } else { |
| len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), |
| len); |
| buf = vmalloc(len); |
| if (!buf) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| buf = vmalloc(len); |
| if (!buf) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len); |
| buf = vmalloc(len); |
| if (!buf) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| if (get_lun_card(chip, lun) != SD_CARD) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); |
| rtsx_trace(chip); |
| 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; |
| } |
| |
| #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)) { |
| set_sense_type(chip, SCSI_LUN(srb), |
| SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| clear = srb->cmnd[2]; |
| |
| buf = vmalloc(scsi_bufflen(srb)); |
| if (!buf) { |
| rtsx_trace(chip); |
| return 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; |
| dev_dbg(rtsx_dev(chip), "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); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| turn_off_led(chip, LED_GPIO); |
| } |
| } else { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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) { |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| rtsx_disable_bus_int(chip); |
| |
| retval = rtsx_read_phy_register(chip, 0x1C, ®); |
| if (retval != STATUS_SUCCESS) { |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| reg |= 0x0001; |
| retval = rtsx_write_phy_register(chip, 0x1C, reg); |
| if (retval != STATUS_SUCCESS) { |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| } else { |
| chip->phy_debug_mode = 0; |
| retval = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0x77); |
| if (retval != STATUS_SUCCESS) { |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| rtsx_enable_bus_int(chip); |
| |
| retval = rtsx_read_phy_register(chip, 0x1C, ®); |
| if (retval != STATUS_SUCCESS) { |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| reg &= 0xFFFE; |
| retval = rtsx_write_phy_register(chip, 0x1C, reg); |
| if (retval != STATUS_SUCCESS) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| if (retval != STATUS_SUCCESS) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR); |
| rtsx_trace(chip); |
| 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) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| } else { |
| set_sense_type(chip, SCSI_LUN(srb), |
| SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| rtsx_trace(chip); |
| 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) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_ERROR; |
| } |
| |
| if (chip->asic_code) { |
| retval = rtsx_read_phy_register(chip, 0x08, &val); |
| if (retval != STATUS_SUCCESS) { |
| vfree(buf); |
| rtsx_trace(chip); |
| return TRANSPORT_ERROR; |
| } |
| |
| retval = rtsx_write_register(chip, PWR_GATE_CTRL, |
| LDO3318_PWR_MASK, LDO_OFF); |
| if (retval != STATUS_SUCCESS) { |
| vfree(buf); |
| rtsx_trace(chip); |
| return TRANSPORT_ERROR; |
| } |
| |
| wait_timeout(600); |
| |
| retval = rtsx_write_phy_register(chip, 0x08, |
| 0x4C00 | chip->phy_voltage); |
| if (retval != STATUS_SUCCESS) { |
| vfree(buf); |
| rtsx_trace(chip); |
| return TRANSPORT_ERROR; |
| } |
| |
| retval = rtsx_write_register(chip, PWR_GATE_CTRL, |
| LDO3318_PWR_MASK, LDO_ON); |
| if (retval != STATUS_SUCCESS) { |
| vfree(buf); |
| rtsx_trace(chip); |
| return TRANSPORT_ERROR; |
| } |
| |
| wait_timeout(600); |
| } |
| |
| retval = card_power_on(chip, SPI_CARD); |
| if (retval != STATUS_SUCCESS) { |
| vfree(buf); |
| rtsx_trace(chip); |
| 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; |
| rtsx_trace(chip); |
| goto exit; |
| } |
| } |
| |
| exit: |
| vfree(buf); |
| |
| retval = card_power_off(chip, SPI_CARD); |
| if (retval != STATUS_SUCCESS) { |
| rtsx_trace(chip); |
| return TRANSPORT_ERROR; |
| } |
| |
| if (chip->asic_code) { |
| retval = rtsx_write_register(chip, PWR_GATE_CTRL, |
| LDO3318_PWR_MASK, LDO_OFF); |
| if (retval != STATUS_SUCCESS) { |
| rtsx_trace(chip); |
| return TRANSPORT_ERROR; |
| } |
| |
| wait_timeout(600); |
| |
| retval = rtsx_write_phy_register(chip, 0x08, val); |
| if (retval != STATUS_SUCCESS) { |
| rtsx_trace(chip); |
| return TRANSPORT_ERROR; |
| } |
| |
| retval = rtsx_write_register(chip, PWR_GATE_CTRL, |
| LDO3318_PWR_MASK, LDO_ON); |
| if (retval != STATUS_SUCCESS) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| buf = vmalloc(len); |
| if (!buf) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len); |
| buf = vmalloc(len); |
| if (!buf) { |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir); |
| |
| if (result != STATUS_SUCCESS) { |
| rtsx_trace(chip); |
| 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; |
| |
| #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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| 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); |
| rtsx_trace(chip); |
| return TRANSPORT_FAILED; |
| } |
| |
| if (chip->card_
|