| /* Driver for Realtek RTS51xx USB card reader |
| * |
| * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2, or (at your option) any |
| * later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, see <http://www.gnu.org/licenses/>. |
| * |
| * Author: |
| * wwang (wei_wang@realsil.com.cn) |
| * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China |
| * Maintainer: |
| * Edwin Rong (edwin_rong@realsil.com.cn) |
| * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China |
| */ |
| |
| #include <linux/blkdev.h> |
| #include <linux/kthread.h> |
| #include <linux/sched.h> |
| #include <linux/slab.h> |
| |
| #include "debug.h" |
| #include "trace.h" |
| #include "rts51x.h" |
| #include "rts51x_transport.h" |
| #include "rts51x_scsi.h" |
| #include "rts51x_card.h" |
| #include "rts51x_chip.h" |
| #include "sd.h" |
| |
| #ifdef SUPPORT_CPRM |
| |
| static inline int get_rsp_type(u8 rsp_code, u8 *rsp_type, int *rsp_len) |
| { |
| if (!rsp_type || !rsp_len) |
| return STATUS_FAIL; |
| |
| switch (rsp_code) { |
| case 0x03: |
| *rsp_type = SD_RSP_TYPE_R0; /* no response */ |
| *rsp_len = 0; |
| break; |
| |
| case 0x04: |
| *rsp_type = SD_RSP_TYPE_R1; /* R1,R6(,R4,R5) */ |
| *rsp_len = 6; |
| break; |
| |
| case 0x05: |
| *rsp_type = SD_RSP_TYPE_R1b; /* R1b */ |
| *rsp_len = 6; |
| break; |
| |
| case 0x06: |
| *rsp_type = SD_RSP_TYPE_R2; /* R2 */ |
| *rsp_len = 17; |
| break; |
| |
| case 0x07: |
| *rsp_type = SD_RSP_TYPE_R3; /* R3 */ |
| *rsp_len = 6; |
| break; |
| |
| default: |
| return STATUS_FAIL; |
| } |
| |
| return STATUS_SUCCESS; |
| } |
| |
| int soft_reset_sd_card(struct rts51x_chip *chip) |
| { |
| return reset_sd(chip); |
| } |
| |
| int ext_sd_send_cmd_get_rsp(struct rts51x_chip *chip, u8 cmd_idx, |
| u32 arg, u8 rsp_type, u8 *rsp, int rsp_len, |
| int special_check) |
| { |
| int retval; |
| int timeout = 50; |
| u16 reg_addr; |
| u8 buf[17], stat; |
| int len = 2; |
| int rty_cnt = 0; |
| |
| RTS51X_DEBUGP("EXT SD/MMC CMD %d\n", cmd_idx); |
| |
| if (rsp_type == SD_RSP_TYPE_R1b) |
| timeout = 3000; |
| |
| RTY_SEND_CMD: |
| |
| rts51x_init_cmd(chip); |
| |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CMD0, 0xFF, 0x40 | cmd_idx); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CMD1, 0xFF, (u8) (arg >> 24)); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CMD2, 0xFF, (u8) (arg >> 16)); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CMD3, 0xFF, (u8) (arg >> 8)); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CMD4, 0xFF, (u8) arg); |
| |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, |
| 0x01, PINGPONG_BUFFER); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_TRANSFER, |
| 0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START); |
| rts51x_add_cmd(chip, CHECK_REG_CMD, SD_TRANSFER, SD_TRANSFER_END, |
| SD_TRANSFER_END); |
| |
| rts51x_add_cmd(chip, READ_REG_CMD, SD_STAT1, 0, 0); |
| |
| if (CHECK_USB(chip, USB_20)) { |
| if (rsp_type == SD_RSP_TYPE_R2) { |
| for (reg_addr = PPBUF_BASE2; |
| reg_addr < PPBUF_BASE2 + 16; reg_addr++) { |
| rts51x_add_cmd(chip, READ_REG_CMD, reg_addr, 0, |
| 0); |
| } |
| len = 19; |
| } else if (rsp_type != SD_RSP_TYPE_R0) { |
| /* Read data from SD_CMDx registers */ |
| for (reg_addr = SD_CMD0; reg_addr <= SD_CMD4; |
| reg_addr++) { |
| rts51x_add_cmd(chip, READ_REG_CMD, reg_addr, 0, |
| 0); |
| } |
| len = 8; |
| } else { |
| len = 3; |
| } |
| rts51x_add_cmd(chip, READ_REG_CMD, SD_CMD5, 0, 0); |
| } else { |
| len = 2; |
| } |
| |
| retval = rts51x_send_cmd(chip, MODE_CR, 100); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, retval); |
| |
| retval = rts51x_get_rsp(chip, len, timeout); |
| |
| if (CHECK_SD_TRANS_FAIL(chip, retval)) { |
| rts51x_clear_sd_error(chip); |
| |
| if (retval == STATUS_TIMEDOUT) { |
| if (rsp_type & SD_WAIT_BUSY_END) { |
| retval = sd_check_data0_status(chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, retval); |
| } |
| } |
| TRACE_RET(chip, STATUS_FAIL); |
| } |
| |
| if (rsp_type == SD_RSP_TYPE_R0) |
| return STATUS_SUCCESS; |
| |
| if (CHECK_USB(chip, USB_20)) { |
| rts51x_read_rsp_buf(chip, 2, buf, len - 2); |
| } else { |
| if (rsp_type == SD_RSP_TYPE_R2) { |
| reg_addr = PPBUF_BASE2; |
| len = 16; |
| } else { |
| reg_addr = SD_CMD0; |
| len = 5; |
| } |
| retval = |
| rts51x_seq_read_register(chip, reg_addr, |
| (unsigned short)len, buf); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, retval); |
| RTS51X_READ_REG(chip, SD_CMD5, buf + len); |
| } |
| stat = chip->rsp_buf[1]; |
| |
| if ((buf[0] & 0xC0) != 0) |
| TRACE_RET(chip, STATUS_FAIL); |
| |
| if (!(rsp_type & SD_NO_CHECK_CRC7)) { |
| if (stat & SD_CRC7_ERR) { |
| if (cmd_idx == WRITE_MULTIPLE_BLOCK) |
| TRACE_RET(chip, STATUS_FAIL); |
| if (rty_cnt < SD_MAX_RETRY_COUNT) { |
| wait_timeout(20); |
| rty_cnt++; |
| goto RTY_SEND_CMD; |
| } else { |
| TRACE_RET(chip, STATUS_FAIL); |
| } |
| } |
| } |
| |
| if ((cmd_idx == SELECT_CARD) || (cmd_idx == APP_CMD) || |
| (cmd_idx == SEND_STATUS) || (cmd_idx == STOP_TRANSMISSION)) { |
| if ((cmd_idx != STOP_TRANSMISSION) && (special_check == 0)) { |
| if (buf[1] & 0x80) |
| TRACE_RET(chip, STATUS_FAIL); |
| } |
| #ifdef SUPPORT_SD_LOCK |
| if (buf[1] & 0x7D) { |
| #else |
| if (buf[1] & 0x7F) { |
| #endif |
| TRACE_RET(chip, STATUS_FAIL); |
| } |
| if (buf[2] & 0xF8) |
| TRACE_RET(chip, STATUS_FAIL); |
| |
| if (cmd_idx == SELECT_CARD) { |
| if (rsp_type == SD_RSP_TYPE_R2) { |
| if ((buf[3] & 0x1E) != 0x04) |
| TRACE_RET(chip, STATUS_FAIL); |
| } else if (rsp_type == SD_RSP_TYPE_R2) { |
| if ((buf[3] & 0x1E) != 0x03) |
| TRACE_RET(chip, STATUS_FAIL); |
| } |
| } |
| } |
| |
| if (rsp && rsp_len) |
| memcpy(rsp, buf, rsp_len); |
| |
| return STATUS_SUCCESS; |
| } |
| |
| int ext_sd_get_rsp(struct rts51x_chip *chip, int len, u8 * rsp, u8 rsp_type) |
| { |
| int retval, rsp_len; |
| u16 reg_addr; |
| |
| if (rsp_type == SD_RSP_TYPE_R0) |
| return STATUS_SUCCESS; |
| |
| rts51x_init_cmd(chip); |
| |
| if (rsp_type == SD_RSP_TYPE_R2) { |
| for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16; |
| reg_addr++) { |
| rts51x_add_cmd(chip, READ_REG_CMD, reg_addr, 0xFF, 0); |
| } |
| rsp_len = 17; |
| } else if (rsp_type != SD_RSP_TYPE_R0) { |
| for (reg_addr = SD_CMD0; reg_addr <= SD_CMD4; reg_addr++) |
| rts51x_add_cmd(chip, READ_REG_CMD, reg_addr, 0xFF, 0); |
| rsp_len = 6; |
| } |
| rts51x_add_cmd(chip, READ_REG_CMD, SD_CMD5, 0xFF, 0); |
| |
| retval = rts51x_send_cmd(chip, MODE_CR, 100); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, retval); |
| |
| retval = rts51x_get_rsp(chip, rsp_len, 100); |
| |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, retval); |
| |
| if (rsp) { |
| int min_len = (rsp_len < len) ? rsp_len : len; |
| |
| memcpy(rsp, rts51x_get_rsp_data(chip), min_len); |
| |
| RTS51X_DEBUGP("min_len = %d\n", min_len); |
| RTS51X_DEBUGP("Response in cmd buf: 0x%x 0x%x 0x%x 0x%x\n", |
| rsp[0], rsp[1], rsp[2], rsp[3]); |
| } |
| |
| return STATUS_SUCCESS; |
| } |
| |
| int ext_sd_execute_no_data(struct rts51x_chip *chip, unsigned int lun, |
| u8 cmd_idx, u8 standby, u8 acmd, u8 rsp_code, |
| u32 arg) |
| { |
| struct sd_info *sd_card = &(chip->sd_card); |
| int retval, rsp_len; |
| u8 rsp_type; |
| |
| retval = sd_switch_clock(chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| |
| if (sd_card->pre_cmd_err) { |
| sd_card->pre_cmd_err = 0; |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| retval = get_rsp_type(rsp_code, &rsp_type, &rsp_len); |
| if (retval != STATUS_SUCCESS) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| sd_card->last_rsp_type = rsp_type; |
| |
| retval = sd_switch_clock(chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| #ifdef SUPPORT_SD_LOCK |
| if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) { |
| if (CHK_MMC_8BIT(sd_card)) { |
| retval = |
| rts51x_write_register(chip, SD_CFG1, 0x03, |
| SD_BUS_WIDTH_8); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) { |
| retval = |
| rts51x_write_register(chip, SD_CFG1, 0x03, |
| SD_BUS_WIDTH_4); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| } |
| #else |
| /* Set H/W SD/MMC Bus Width */ |
| rts51x_write_register(chip, SD_CFG1, 0x03, SD_BUS_WIDTH_4); |
| #endif |
| |
| if (standby) { |
| retval = sd_select_card(chip, 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Cmd_Failed); |
| } |
| |
| if (acmd) { |
| retval = |
| ext_sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, |
| SD_RSP_TYPE_R1, NULL, 0, 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Cmd_Failed); |
| } |
| |
| retval = ext_sd_send_cmd_get_rsp(chip, cmd_idx, arg, rsp_type, |
| sd_card->rsp, rsp_len, 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Cmd_Failed); |
| |
| if (standby) { |
| retval = sd_select_card(chip, 1); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Cmd_Failed); |
| } |
| #ifdef SUPPORT_SD_LOCK |
| /* Get SD lock status */ |
| retval = sd_update_lock_status(chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Cmd_Failed); |
| #endif |
| |
| return TRANSPORT_GOOD; |
| |
| SD_Execute_Cmd_Failed: |
| sd_card->pre_cmd_err = 1; |
| set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE); |
| release_sd_card(chip); |
| do_reset_sd_card(chip); |
| if (!(chip->card_ready & SD_CARD)) |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| int ext_sd_execute_read_data(struct rts51x_chip *chip, unsigned int lun, |
| u8 cmd_idx, u8 cmd12, u8 standby, |
| u8 acmd, u8 rsp_code, u32 arg, u32 data_len, |
| void *data_buf, unsigned int buf_len, int use_sg) |
| { |
| struct sd_info *sd_card = &(chip->sd_card); |
| int retval, rsp_len, i; |
| int cmd13_checkbit = 0, read_err = 0; |
| u8 rsp_type, bus_width; |
| |
| if (sd_card->pre_cmd_err) { |
| sd_card->pre_cmd_err = 0; |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| retval = sd_switch_clock(chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, STATUS_FAIL); |
| retval = get_rsp_type(rsp_code, &rsp_type, &rsp_len); |
| if (retval != STATUS_SUCCESS) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| sd_card->last_rsp_type = rsp_type; |
| |
| retval = sd_switch_clock(chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| #ifdef SUPPORT_SD_LOCK |
| if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) { |
| if (CHK_MMC_8BIT(sd_card)) |
| bus_width = SD_BUS_WIDTH_8; |
| else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) |
| bus_width = SD_BUS_WIDTH_4; |
| else |
| bus_width = SD_BUS_WIDTH_1; |
| } else { |
| bus_width = SD_BUS_WIDTH_4; |
| } |
| RTS51X_DEBUGP("bus_width = %d\n", bus_width); |
| #else |
| bus_width = SD_BUS_WIDTH_4; |
| #endif |
| |
| if (data_len < 512) { |
| retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, data_len, |
| SD_RSP_TYPE_R1, NULL, 0, 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| } |
| |
| if (standby) { |
| retval = sd_select_card(chip, 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| } |
| |
| if (acmd) { |
| retval = |
| ext_sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, |
| SD_RSP_TYPE_R1, NULL, 0, 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| } |
| |
| if (data_len <= 512) { |
| int min_len; |
| u8 *buf; |
| u16 byte_cnt, blk_cnt; |
| u8 cmd[5]; |
| unsigned int offset = 0; |
| void *sg = NULL; |
| |
| byte_cnt = (u16) (data_len & 0x3FF); |
| blk_cnt = 1; |
| |
| cmd[0] = 0x40 | cmd_idx; |
| cmd[1] = (u8) (arg >> 24); |
| cmd[2] = (u8) (arg >> 16); |
| cmd[3] = (u8) (arg >> 8); |
| cmd[4] = (u8) arg; |
| |
| buf = kmalloc(data_len, GFP_KERNEL); |
| if (buf == NULL) |
| TRACE_RET(chip, TRANSPORT_ERROR); |
| |
| retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, byte_cnt, |
| blk_cnt, bus_width, buf, data_len, 2000); |
| if (retval != STATUS_SUCCESS) { |
| read_err = 1; |
| kfree(buf); |
| rts51x_write_register(chip, CARD_STOP, |
| SD_STOP | SD_CLR_ERR, |
| SD_STOP | SD_CLR_ERR); |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| } |
| |
| min_len = min(data_len, buf_len); |
| if (use_sg) |
| rts51x_access_sglist(buf, min_len, (void *)data_buf, |
| &sg, &offset, TO_XFER_BUF); |
| else |
| memcpy(data_buf, buf, min_len); |
| |
| kfree(buf); |
| } else if (!(data_len & 0x1FF)) { |
| rts51x_init_cmd(chip); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_BLOCK_CNT_H, |
| 0xFF, (u8) (data_len >> 17)); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_BLOCK_CNT_L, |
| 0xFF, (u8) ((data_len & 0x0001FE00) >> 9)); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CMD0, 0xFF, |
| 0x40 | cmd_idx); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CMD1, 0xFF, |
| (u8) (arg >> 24)); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CMD2, 0xFF, |
| (u8) (arg >> 16)); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CMD3, 0xFF, |
| (u8) (arg >> 8)); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CMD4, 0xFF, (u8) arg); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CFG1, 0x03, bus_width); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type); |
| trans_dma_enable(DMA_FROM_DEVICE, chip, data_len, DMA_512); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_TRANSFER, 0xFF, |
| SD_TM_AUTO_READ_2 | SD_TRANSFER_START); |
| rts51x_add_cmd(chip, CHECK_REG_CMD, SD_TRANSFER, |
| SD_TRANSFER_END, SD_TRANSFER_END); |
| retval = rts51x_send_cmd(chip, MODE_CDIR, 100); |
| if (retval != STATUS_SUCCESS) { |
| read_err = 1; |
| rts51x_ep0_write_register(chip, CARD_STOP, |
| SD_STOP | SD_CLR_ERR, |
| SD_STOP | SD_CLR_ERR); |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| } |
| |
| retval = |
| rts51x_transfer_data_rcc(chip, RCV_BULK_PIPE(chip), |
| data_buf, buf_len, use_sg, NULL, |
| 10000, STAGE_DI); |
| if (retval != STATUS_SUCCESS) { |
| read_err = 1; |
| rts51x_ep0_write_register(chip, CARD_STOP, |
| SD_STOP | SD_CLR_ERR, |
| SD_STOP | SD_CLR_ERR); |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| } |
| retval = rts51x_get_rsp(chip, 1, 500); |
| if (CHECK_SD_TRANS_FAIL(chip, retval)) { |
| read_err = 1; |
| rts51x_ep0_write_register(chip, CARD_STOP, |
| SD_STOP | SD_CLR_ERR, |
| SD_STOP | SD_CLR_ERR); |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| } |
| } else { |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| } |
| |
| retval = ext_sd_get_rsp(chip, rsp_len, sd_card->rsp, rsp_type); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| |
| if (standby) { |
| retval = sd_select_card(chip, 1); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| } |
| |
| if (cmd12) { |
| retval = ext_sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION, |
| 0, SD_RSP_TYPE_R1b, NULL, 0, |
| 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| } |
| |
| if (data_len < 512) { |
| retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200, |
| SD_RSP_TYPE_R1, NULL, 0, 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| |
| rts51x_write_register(chip, SD_BYTE_CNT_H, 0xFF, 0x02); |
| rts51x_write_register(chip, SD_BYTE_CNT_L, 0xFF, 0x00); |
| } |
| |
| if (standby || cmd12) |
| cmd13_checkbit = 1; |
| |
| for (i = 0; i < 3; i++) { |
| retval = |
| ext_sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, |
| SD_RSP_TYPE_R1, NULL, 0, |
| cmd13_checkbit); |
| if (retval == STATUS_SUCCESS) |
| break; |
| } |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed); |
| |
| return TRANSPORT_GOOD; |
| |
| SD_Execute_Read_Cmd_Failed: |
| sd_card->pre_cmd_err = 1; |
| set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE); |
| if (read_err) |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR); |
| release_sd_card(chip); |
| do_reset_sd_card(chip); |
| if (!(chip->card_ready & SD_CARD)) |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| int ext_sd_execute_write_data(struct rts51x_chip *chip, unsigned int lun, |
| u8 cmd_idx, u8 cmd12, u8 standby, u8 acmd, |
| u8 rsp_code, u32 arg, u32 data_len, |
| void *data_buf, unsigned int buf_len, int use_sg) |
| { |
| struct sd_info *sd_card = &(chip->sd_card); |
| int retval, rsp_len; |
| int cmd13_checkbit = 0, write_err = 0; |
| u8 rsp_type; |
| u32 i; |
| #ifdef SUPPORT_SD_LOCK |
| int lock_cmd_fail = 0; |
| u8 sd_lock_state = 0; |
| u8 lock_cmd_type = 0; |
| #endif |
| |
| if (sd_card->pre_cmd_err) { |
| sd_card->pre_cmd_err = 0; |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| retval = sd_switch_clock(chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, STATUS_FAIL); |
| #ifdef SUPPORT_SD_LOCK |
| if (cmd_idx == LOCK_UNLOCK) { |
| sd_lock_state = sd_card->sd_lock_status; |
| sd_lock_state &= SD_LOCKED; |
| } |
| #endif |
| |
| retval = get_rsp_type(rsp_code, &rsp_type, &rsp_len); |
| if (retval != STATUS_SUCCESS) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| sd_card->last_rsp_type = rsp_type; |
| |
| retval = sd_switch_clock(chip); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| #ifdef SUPPORT_SD_LOCK |
| if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) { |
| if (CHK_MMC_8BIT(sd_card)) { |
| retval = |
| rts51x_write_register(chip, SD_CFG1, 0x03, |
| SD_BUS_WIDTH_8); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) { |
| retval = |
| rts51x_write_register(chip, SD_CFG1, 0x03, |
| SD_BUS_WIDTH_4); |
| if (retval != STATUS_SUCCESS) |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| } |
| #else |
| rts51x_write_register(chip, SD_CFG1, 0x03, SD_BUS_WIDTH_4); |
| #endif |
| |
| if (data_len < 512) { |
| retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, data_len, |
| SD_RSP_TYPE_R1, NULL, 0, 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| } |
| |
| if (standby) { |
| retval = sd_select_card(chip, 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| } |
| |
| if (acmd) { |
| retval = |
| ext_sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, |
| SD_RSP_TYPE_R1, NULL, 0, 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| } |
| |
| retval = ext_sd_send_cmd_get_rsp(chip, cmd_idx, arg, rsp_type, |
| sd_card->rsp, rsp_len, 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| |
| if (data_len <= 512) { |
| u8 *buf; |
| unsigned int offset = 0; |
| void *sg = NULL; |
| |
| buf = kmalloc(data_len, GFP_KERNEL); |
| if (buf == NULL) |
| TRACE_RET(chip, TRANSPORT_ERROR); |
| |
| if (use_sg) |
| rts51x_access_sglist(buf, data_len, (void *)data_buf, |
| &sg, &offset, FROM_XFER_BUF); |
| else |
| memcpy(buf, data_buf, data_len); |
| |
| #ifdef SUPPORT_SD_LOCK |
| if (cmd_idx == LOCK_UNLOCK) |
| lock_cmd_type = buf[0] & 0x0F; |
| #endif |
| |
| if (data_len > 256) { |
| rts51x_init_cmd(chip); |
| for (i = 0; i < 256; i++) { |
| rts51x_add_cmd(chip, WRITE_REG_CMD, |
| (u16) (PPBUF_BASE2 + i), 0xFF, |
| buf[i]); |
| } |
| retval = rts51x_send_cmd(chip, MODE_C, 250); |
| if (retval != STATUS_SUCCESS) { |
| kfree(buf); |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| } |
| |
| rts51x_init_cmd(chip); |
| for (i = 256; i < data_len; i++) { |
| rts51x_add_cmd(chip, WRITE_REG_CMD, |
| (u16) (PPBUF_BASE2 + i), 0xFF, |
| buf[i]); |
| } |
| retval = rts51x_send_cmd(chip, MODE_C, 250); |
| if (retval != STATUS_SUCCESS) { |
| kfree(buf); |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| } |
| } else { |
| rts51x_init_cmd(chip); |
| for (i = 0; i < data_len; i++) { |
| rts51x_add_cmd(chip, WRITE_REG_CMD, |
| (u16) (PPBUF_BASE2 + i), 0xFF, |
| buf[i]); |
| } |
| retval = rts51x_send_cmd(chip, MODE_C, 250); |
| if (retval != STATUS_SUCCESS) { |
| kfree(buf); |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| } |
| } |
| |
| kfree(buf); |
| |
| rts51x_init_cmd(chip); |
| |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, |
| (u8) ((data_len >> 8) & 0x03)); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, |
| (u8) data_len); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0x00); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 0x01); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, |
| PINGPONG_BUFFER); |
| |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_TRANSFER, 0xFF, |
| SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START); |
| rts51x_add_cmd(chip, CHECK_REG_CMD, SD_TRANSFER, |
| SD_TRANSFER_END, SD_TRANSFER_END); |
| |
| retval = rts51x_send_cmd(chip, MODE_CR, 100); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| |
| retval = rts51x_get_rsp(chip, 1, 250); |
| if (CHECK_SD_TRANS_FAIL(chip, retval)) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| } else if (!(data_len & 0x1FF)) { |
| rts51x_init_cmd(chip); |
| |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_BLOCK_CNT_H, |
| 0xFF, (u8) (data_len >> 17)); |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_BLOCK_CNT_L, |
| 0xFF, (u8) ((data_len & 0x0001FE00) >> 9)); |
| |
| trans_dma_enable(DMA_TO_DEVICE, chip, data_len, DMA_512); |
| |
| rts51x_add_cmd(chip, WRITE_REG_CMD, SD_TRANSFER, 0xFF, |
| SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START); |
| rts51x_add_cmd(chip, CHECK_REG_CMD, SD_TRANSFER, |
| SD_TRANSFER_END, SD_TRANSFER_END); |
| |
| retval = rts51x_send_cmd(chip, MODE_CDOR, 100); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| |
| retval = |
| rts51x_transfer_data_rcc(chip, SND_BULK_PIPE(chip), |
| data_buf, buf_len, use_sg, NULL, |
| 10000, STAGE_DO); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| |
| retval = rts51x_get_rsp(chip, 1, 10000); |
| if (CHECK_SD_TRANS_FAIL(chip, retval)) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| |
| } else { |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| } |
| |
| if (retval < 0) { |
| write_err = 1; |
| rts51x_write_register(chip, CARD_STOP, SD_STOP | SD_CLR_ERR, |
| SD_STOP | SD_CLR_ERR); |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| } |
| #ifdef SUPPORT_SD_LOCK |
| if (cmd_idx == LOCK_UNLOCK) { |
| if (lock_cmd_type == SD_ERASE) { |
| sd_card->sd_erase_status = SD_UNDER_ERASING; |
| scsi_set_resid(srb, 0); |
| return TRANSPORT_GOOD; |
| } |
| |
| rts51x_init_cmd(chip); |
| rts51x_add_cmd(chip, CHECK_REG_CMD, SD_BUS_STAT, SD_DAT0_STATUS, |
| SD_DAT0_STATUS); |
| retval = rts51x_send_cmd(chip, MODE_CR, 250); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| rts51x_get_rsp(chip, 1, 200); /* Don't care return value */ |
| |
| retval = sd_update_lock_status(chip); |
| if (retval != STATUS_SUCCESS) { |
| RTS51X_DEBUGP("Lock command fail!\n"); |
| lock_cmd_fail = 1; |
| } |
| } |
| #endif /* SUPPORT_SD_LOCK */ |
| |
| if (standby) { |
| retval = sd_select_card(chip, 1); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| } |
| |
| if (cmd12) { |
| retval = ext_sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION, |
| 0, SD_RSP_TYPE_R1b, NULL, 0, |
| 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| } |
| |
| if (data_len < 512) { |
| retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200, |
| SD_RSP_TYPE_R1, NULL, 0, 0); |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| |
| rts51x_write_register(chip, SD_BYTE_CNT_H, 0xFF, 0x02); |
| rts51x_write_register(chip, SD_BYTE_CNT_L, 0xFF, 0x00); |
| } |
| |
| if (cmd12 || standby) { |
| /* There is CMD7 or CMD12 sent before CMD13 */ |
| cmd13_checkbit = 1; |
| } |
| |
| for (i = 0; i < 3; i++) { |
| retval = |
| ext_sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, |
| SD_RSP_TYPE_R1, NULL, 0, |
| cmd13_checkbit); |
| if (retval == STATUS_SUCCESS) |
| break; |
| } |
| if (retval != STATUS_SUCCESS) |
| TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed); |
| #ifdef SUPPORT_SD_LOCK |
| if (cmd_idx == LOCK_UNLOCK) { |
| if (!lock_cmd_fail) { |
| RTS51X_DEBUGP("lock_cmd_type = 0x%x\n", |
| lock_cmd_type); |
| if (lock_cmd_type & SD_CLR_PWD) |
| sd_card->sd_lock_status &= ~SD_PWD_EXIST; |
| if (lock_cmd_type & SD_SET_PWD) |
| sd_card->sd_lock_status |= SD_PWD_EXIST; |
| } |
| |
| RTS51X_DEBUGP("sd_lock_state = 0x%x," |
| "sd_card->sd_lock_status = 0x%x\n", |
| sd_lock_state, sd_card->sd_lock_status); |
| if (sd_lock_state ^ (sd_card->sd_lock_status & SD_LOCKED)) { |
| sd_card->sd_lock_notify = 1; |
| if (sd_lock_state) { |
| if (sd_card->sd_lock_status & |
| SD_LOCK_1BIT_MODE) { |
| sd_card->sd_lock_status |= |
| (SD_UNLOCK_POW_ON | SD_SDR_RST); |
| if (CHK_SD(sd_card)) { |
| retval = reset_sd(chip); |
| if (retval != STATUS_SUCCESS) { |
| sd_card->sd_lock_status |
| &= ~(SD_UNLOCK_POW_ON | |
| SD_SDR_RST); |
| TRACE_GOTO(chip, |
| SD_Execute_Write_Cmd_Failed); |
| } |
| } |
| |
| sd_card->sd_lock_status &= |
| ~(SD_UNLOCK_POW_ON | SD_SDR_RST); |
| } |
| } |
| } |
| } |
| |
| if (lock_cmd_fail) { |
| scsi_set_resid(srb, 0); |
| set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| #endif /* SUPPORT_SD_LOCK */ |
| |
| return TRANSPORT_GOOD; |
| |
| SD_Execute_Write_Cmd_Failed: |
| sd_card->pre_cmd_err = 1; |
| set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE); |
| if (write_err) |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR); |
| release_sd_card(chip); |
| do_reset_sd_card(chip); |
| if (!(chip->card_ready & SD_CARD)) |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| int sd_pass_thru_mode(struct scsi_cmnd *srb, struct rts51x_chip *chip) |
| { |
| struct sd_info *sd_card = &(chip->sd_card); |
| unsigned int lun = SCSI_LUN(srb); |
| int len; |
| u8 buf[18] = { |
| 0x00, |
| 0x00, |
| 0x00, |
| 0x0E, |
| 0x00, /* Version Number */ |
| 0x00, /* WP | Media Type */ |
| 0x00, /* RCA (Low byte) */ |
| 0x00, /* RCA (High byte) */ |
| 0x53, /* 'S' */ |
| 0x44, /* 'D' */ |
| 0x20, /* ' ' */ |
| 0x43, /* 'C' */ |
| 0x61, /* 'a' */ |
| 0x72, /* 'r' */ |
| 0x64, /* 'd' */ |
| 0x00, /* Max LUN Number */ |
| 0x00, |
| 0x00, |
| }; |
| |
| sd_card->pre_cmd_err = 0; |
| |
| if (!(CHK_BIT(chip->lun_mc, lun))) { |
| SET_BIT(chip->lun_mc, lun); |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if ((0x53 != srb->cmnd[2]) || (0x44 != srb->cmnd[3]) |
| || (0x20 != srb->cmnd[4]) || (0x43 != srb->cmnd[5]) |
| || (0x61 != srb->cmnd[6]) || (0x72 != srb->cmnd[7]) |
| || (0x64 != srb->cmnd[8])) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| switch (srb->cmnd[1] & 0x0F) { |
| case 0: |
| sd_card->sd_pass_thru_en = 0; |
| break; |
| |
| case 1: |
| sd_card->sd_pass_thru_en = 1; |
| break; |
| |
| default: |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| /* 0x01:SD Memory Card; 0x02:Other Media; 0x03:Illegal Media; */ |
| buf[5] = (1 == CHK_SD(sd_card)) ? 0x01 : 0x02; |
| if (chip->card_wp & SD_CARD) |
| buf[5] |= 0x80; |
| |
| buf[6] = (u8) (sd_card->sd_addr >> 16); |
| buf[7] = (u8) (sd_card->sd_addr >> 24); |
| |
| buf[15] = chip->max_lun; |
| |
| len = min(18, (int)scsi_bufflen(srb)); |
| rts51x_set_xfer_buf(buf, len, srb); |
| |
| return TRANSPORT_GOOD; |
| } |
| |
| int sd_execute_no_data(struct scsi_cmnd *srb, struct rts51x_chip *chip) |
| { |
| struct sd_info *sd_card = &(chip->sd_card); |
| unsigned int lun = SCSI_LUN(srb); |
| int retval; |
| u8 cmd_idx, rsp_code; |
| u8 standby = 0, acmd = 0; |
| u32 arg; |
| |
| if (!sd_card->sd_pass_thru_en) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| cmd_idx = srb->cmnd[2] & 0x3F; |
| if (srb->cmnd[1] & 0x02) |
| standby = 1; |
| if (srb->cmnd[1] & 0x01) |
| acmd = 1; |
| |
| arg = ((u32) srb->cmnd[3] << 24) | ((u32) srb->cmnd[4] << 16) | |
| ((u32) srb->cmnd[5] << 8) | srb->cmnd[6]; |
| |
| rsp_code = srb->cmnd[10]; |
| |
| retval = |
| ext_sd_execute_no_data(chip, lun, cmd_idx, standby, acmd, rsp_code, |
| arg); |
| scsi_set_resid(srb, 0); |
| return retval; |
| } |
| |
| int sd_execute_read_data(struct scsi_cmnd *srb, struct rts51x_chip *chip) |
| { |
| struct sd_info *sd_card = &(chip->sd_card); |
| int retval; |
| unsigned int lun = SCSI_LUN(srb); |
| u8 cmd_idx, rsp_code, send_cmd12 = 0, standby = 0, acmd = 0; |
| u32 arg, data_len; |
| |
| if (!sd_card->sd_pass_thru_en) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| cmd_idx = srb->cmnd[2] & 0x3F; |
| if (srb->cmnd[1] & 0x04) |
| send_cmd12 = 1; |
| if (srb->cmnd[1] & 0x02) |
| standby = 1; |
| if (srb->cmnd[1] & 0x01) |
| acmd = 1; |
| |
| arg = ((u32) srb->cmnd[3] << 24) | ((u32) srb->cmnd[4] << 16) | |
| ((u32) srb->cmnd[5] << 8) | srb->cmnd[6]; |
| |
| data_len = |
| ((u32) srb->cmnd[7] << 16) | ((u32) srb->cmnd[8] << 8) | |
| srb->cmnd[9]; |
| rsp_code = srb->cmnd[10]; |
| |
| retval = |
| ext_sd_execute_read_data(chip, lun, cmd_idx, send_cmd12, standby, |
| acmd, rsp_code, arg, data_len, |
| scsi_sglist(srb), scsi_bufflen(srb), |
| scsi_sg_count(srb)); |
| scsi_set_resid(srb, 0); |
| return retval; |
| } |
| |
| int sd_execute_write_data(struct scsi_cmnd *srb, struct rts51x_chip *chip) |
| { |
| struct sd_info *sd_card = &(chip->sd_card); |
| int retval; |
| unsigned int lun = SCSI_LUN(srb); |
| u8 cmd_idx, rsp_code, send_cmd12 = 0, standby = 0, acmd = 0; |
| u32 data_len, arg; |
| |
| if (!sd_card->sd_pass_thru_en) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| cmd_idx = srb->cmnd[2] & 0x3F; |
| if (srb->cmnd[1] & 0x04) |
| send_cmd12 = 1; |
| if (srb->cmnd[1] & 0x02) |
| standby = 1; |
| if (srb->cmnd[1] & 0x01) |
| acmd = 1; |
| |
| data_len = |
| ((u32) srb->cmnd[7] << 16) | ((u32) srb->cmnd[8] << 8) | |
| srb->cmnd[9]; |
| arg = |
| ((u32) srb->cmnd[3] << 24) | ((u32) srb->cmnd[4] << 16) | |
| ((u32) srb->cmnd[5] << 8) | srb->cmnd[6]; |
| rsp_code = srb->cmnd[10]; |
| |
| retval = |
| ext_sd_execute_write_data(chip, lun, cmd_idx, send_cmd12, standby, |
| acmd, rsp_code, arg, data_len, |
| scsi_sglist(srb), scsi_bufflen(srb), |
| scsi_sg_count(srb)); |
| scsi_set_resid(srb, 0); |
| return retval; |
| } |
| |
| int sd_get_cmd_rsp(struct scsi_cmnd *srb, struct rts51x_chip *chip) |
| { |
| struct sd_info *sd_card = &(chip->sd_card); |
| unsigned int lun = SCSI_LUN(srb); |
| int count; |
| u16 data_len; |
| |
| if (!sd_card->sd_pass_thru_en) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if (sd_card->pre_cmd_err) { |
| sd_card->pre_cmd_err = 0; |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| data_len = ((u16) srb->cmnd[7] << 8) | srb->cmnd[8]; |
| |
| if (sd_card->last_rsp_type == SD_RSP_TYPE_R0) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } else if (sd_card->last_rsp_type == SD_RSP_TYPE_R2) { |
| count = (data_len < 17) ? data_len : 17; |
| } else { |
| count = (data_len < 6) ? data_len : 6; |
| } |
| rts51x_set_xfer_buf(sd_card->rsp, count, srb); |
| |
| RTS51X_DEBUGP("Response length: %d\n", data_len); |
| RTS51X_DEBUGP("Response: 0x%x 0x%x 0x%x 0x%x\n", |
| sd_card->rsp[0], sd_card->rsp[1], sd_card->rsp[2], |
| sd_card->rsp[3]); |
| |
| scsi_set_resid(srb, 0); |
| return TRANSPORT_GOOD; |
| } |
| |
| int sd_hw_rst(struct scsi_cmnd *srb, struct rts51x_chip *chip) |
| { |
| struct sd_info *sd_card = &(chip->sd_card); |
| unsigned int lun = SCSI_LUN(srb); |
| int retval; |
| |
| if (!sd_card->sd_pass_thru_en) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if (sd_card->pre_cmd_err) { |
| sd_card->pre_cmd_err = 0; |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| if ((0x53 != srb->cmnd[2]) || (0x44 != srb->cmnd[3]) |
| || (0x20 != srb->cmnd[4]) || (0x43 != srb->cmnd[5]) |
| || (0x61 != srb->cmnd[6]) || (0x72 != srb->cmnd[7]) |
| || (0x64 != srb->cmnd[8])) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| switch (srb->cmnd[1] & 0x0F) { |
| case 0: |
| /* SD Card Power Off -> ON and Initialization */ |
| #ifdef SUPPORT_SD_LOCK |
| if (0x64 == srb->cmnd[9]) { |
| /* Command Mode */ |
| sd_card->sd_lock_status |= SD_SDR_RST; |
| } |
| #endif /* SUPPORT_SD_LOCK */ |
| retval = reset_sd_card(chip); |
| if (retval != STATUS_SUCCESS) { |
| #ifdef SUPPORT_SD_LOCK |
| sd_card->sd_lock_status &= ~SD_SDR_RST; |
| #endif |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| sd_card->pre_cmd_err = 1; |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| #ifdef SUPPORT_SD_LOCK |
| sd_card->sd_lock_status &= ~SD_SDR_RST; |
| #endif |
| break; |
| |
| case 1: |
| /* reset CMD(CMD0) and Initialization |
| * (without SD Card Power Off -> ON) */ |
| retval = soft_reset_sd_card(chip); |
| if (retval != STATUS_SUCCESS) { |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT); |
| sd_card->pre_cmd_err = 1; |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| break; |
| |
| default: |
| set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD); |
| TRACE_RET(chip, TRANSPORT_FAILED); |
| } |
| |
| scsi_set_resid(srb, 0); |
| return TRANSPORT_GOOD; |
| } |
| #endif |