| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Driver for Realtek PCI-Express card reader |
| * |
| * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. |
| * |
| * Author: |
| * Wei WANG (wei_wang@realsil.com.cn) |
| * Micky Ching (micky_ching@realsil.com.cn) |
| */ |
| |
| #include <linux/blkdev.h> |
| #include <linux/kthread.h> |
| #include <linux/sched.h> |
| |
| #include "rtsx.h" |
| |
| /*********************************************************************** |
| * Scatter-gather transfer buffer access routines |
| ***********************************************************************/ |
| |
| /* |
| * Copy a buffer of length buflen to/from the srb's transfer buffer. |
| * (Note: for scatter-gather transfers (srb->use_sg > 0), srb->request_buffer |
| * points to a list of s-g entries and we ignore srb->request_bufflen. |
| * For non-scatter-gather transfers, srb->request_buffer points to the |
| * transfer buffer itself and srb->request_bufflen is the buffer's length.) |
| * Update the *index and *offset variables so that the next copy will |
| * pick up from where this one left off. |
| */ |
| |
| unsigned int rtsx_stor_access_xfer_buf(unsigned char *buffer, |
| unsigned int buflen, |
| struct scsi_cmnd *srb, |
| unsigned int *index, |
| unsigned int *offset, |
| enum xfer_buf_dir dir) |
| { |
| unsigned int cnt; |
| |
| /* If not using scatter-gather, just transfer the data directly. */ |
| if (scsi_sg_count(srb) == 0) { |
| unsigned char *sgbuffer; |
| |
| if (*offset >= scsi_bufflen(srb)) |
| return 0; |
| cnt = min(buflen, scsi_bufflen(srb) - *offset); |
| |
| sgbuffer = (unsigned char *)scsi_sglist(srb) + *offset; |
| |
| if (dir == TO_XFER_BUF) |
| memcpy(sgbuffer, buffer, cnt); |
| else |
| memcpy(buffer, sgbuffer, cnt); |
| *offset += cnt; |
| |
| /* |
| * Using scatter-gather. We have to go through the list one entry |
| * at a time. Each s-g entry contains some number of pages, and |
| * each page has to be kmap()'ed separately. |
| */ |
| } else { |
| struct scatterlist *sg = |
| (struct scatterlist *)scsi_sglist(srb) |
| + *index; |
| |
| /* |
| * This loop handles a single s-g list entry, which may |
| * include multiple pages. Find the initial page structure |
| * and the starting offset within the page, and update |
| * the *offset and *index values for the next loop. |
| */ |
| cnt = 0; |
| while (cnt < buflen && *index < scsi_sg_count(srb)) { |
| struct page *page = sg_page(sg) + |
| ((sg->offset + *offset) >> PAGE_SHIFT); |
| unsigned int poff = (sg->offset + *offset) & |
| (PAGE_SIZE - 1); |
| unsigned int sglen = sg->length - *offset; |
| |
| if (sglen > buflen - cnt) { |
| /* Transfer ends within this s-g entry */ |
| sglen = buflen - cnt; |
| *offset += sglen; |
| } else { |
| /* Transfer continues to next s-g entry */ |
| *offset = 0; |
| ++*index; |
| ++sg; |
| } |
| |
| while (sglen > 0) { |
| unsigned int plen = min(sglen, (unsigned int) |
| PAGE_SIZE - poff); |
| unsigned char *ptr = kmap(page); |
| |
| if (dir == TO_XFER_BUF) |
| memcpy(ptr + poff, buffer + cnt, plen); |
| else |
| memcpy(buffer + cnt, ptr + poff, plen); |
| kunmap(page); |
| |
| /* Start at the beginning of the next page */ |
| poff = 0; |
| ++page; |
| cnt += plen; |
| sglen -= plen; |
| } |
| } |
| } |
| |
| /* Return the amount actually transferred */ |
| return cnt; |
| } |
| |
| /* |
| * Store the contents of buffer into srb's transfer buffer and set the |
| * SCSI residue. |
| */ |
| void rtsx_stor_set_xfer_buf(unsigned char *buffer, |
| unsigned int buflen, struct scsi_cmnd *srb) |
| { |
| unsigned int index = 0, offset = 0; |
| |
| rtsx_stor_access_xfer_buf(buffer, buflen, srb, &index, &offset, |
| TO_XFER_BUF); |
| if (buflen < scsi_bufflen(srb)) |
| scsi_set_resid(srb, scsi_bufflen(srb) - buflen); |
| } |
| |
| void rtsx_stor_get_xfer_buf(unsigned char *buffer, |
| unsigned int buflen, struct scsi_cmnd *srb) |
| { |
| unsigned int index = 0, offset = 0; |
| |
| rtsx_stor_access_xfer_buf(buffer, buflen, srb, &index, &offset, |
| FROM_XFER_BUF); |
| if (buflen < scsi_bufflen(srb)) |
| scsi_set_resid(srb, scsi_bufflen(srb) - buflen); |
| } |
| |
| /*********************************************************************** |
| * Transport routines |
| ***********************************************************************/ |
| |
| /* |
| * Invoke the transport and basic error-handling/recovery methods |
| * |
| * This is used to send the message to the device and receive the response. |
| */ |
| void rtsx_invoke_transport(struct scsi_cmnd *srb, struct rtsx_chip *chip) |
| { |
| int result; |
| |
| result = rtsx_scsi_handler(srb, chip); |
| |
| /* |
| * if the command gets aborted by the higher layers, we need to |
| * short-circuit all other processing. |
| */ |
| if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) { |
| dev_dbg(rtsx_dev(chip), "-- command was aborted\n"); |
| srb->result = DID_ABORT << 16; |
| goto handle_errors; |
| } |
| |
| /* if there is a transport error, reset and don't auto-sense */ |
| if (result == TRANSPORT_ERROR) { |
| dev_dbg(rtsx_dev(chip), "-- transport indicates error, resetting\n"); |
| srb->result = DID_ERROR << 16; |
| goto handle_errors; |
| } |
| |
| srb->result = SAM_STAT_GOOD; |
| |
| /* |
| * If we have a failure, we're going to do a REQUEST_SENSE |
| * automatically. Note that we differentiate between a command |
| * "failure" and an "error" in the transport mechanism. |
| */ |
| if (result == TRANSPORT_FAILED) { |
| /* set the result so the higher layers expect this data */ |
| srb->result = SAM_STAT_CHECK_CONDITION; |
| memcpy(srb->sense_buffer, |
| (unsigned char *)&chip->sense_buffer[SCSI_LUN(srb)], |
| sizeof(struct sense_data_t)); |
| } |
| |
| return; |
| |
| handle_errors: |
| return; |
| } |
| |
| void rtsx_add_cmd(struct rtsx_chip *chip, |
| u8 cmd_type, u16 reg_addr, u8 mask, u8 data) |
| { |
| __le32 *cb = (__le32 *)(chip->host_cmds_ptr); |
| u32 val = 0; |
| |
| val |= (u32)(cmd_type & 0x03) << 30; |
| val |= (u32)(reg_addr & 0x3FFF) << 16; |
| val |= (u32)mask << 8; |
| val |= (u32)data; |
| |
| spin_lock_irq(&chip->rtsx->reg_lock); |
| if (chip->ci < (HOST_CMDS_BUF_LEN / 4)) |
| cb[(chip->ci)++] = cpu_to_le32(val); |
| |
| spin_unlock_irq(&chip->rtsx->reg_lock); |
| } |
| |
| void rtsx_send_cmd_no_wait(struct rtsx_chip *chip) |
| { |
| u32 val = BIT(31); |
| |
| rtsx_writel(chip, RTSX_HCBAR, chip->host_cmds_addr); |
| |
| val |= (u32)(chip->ci * 4) & 0x00FFFFFF; |
| /* Hardware Auto Response */ |
| val |= 0x40000000; |
| rtsx_writel(chip, RTSX_HCBCTLR, val); |
| } |
| |
| int rtsx_send_cmd(struct rtsx_chip *chip, u8 card, int timeout) |
| { |
| struct rtsx_dev *rtsx = chip->rtsx; |
| struct completion trans_done; |
| u32 val = BIT(31); |
| long timeleft; |
| int err = 0; |
| |
| if (card == SD_CARD) |
| rtsx->check_card_cd = SD_EXIST; |
| else if (card == MS_CARD) |
| rtsx->check_card_cd = MS_EXIST; |
| else if (card == XD_CARD) |
| rtsx->check_card_cd = XD_EXIST; |
| else |
| rtsx->check_card_cd = 0; |
| |
| spin_lock_irq(&rtsx->reg_lock); |
| |
| /* set up data structures for the wakeup system */ |
| rtsx->done = &trans_done; |
| rtsx->trans_result = TRANS_NOT_READY; |
| init_completion(&trans_done); |
| rtsx->trans_state = STATE_TRANS_CMD; |
| |
| rtsx_writel(chip, RTSX_HCBAR, chip->host_cmds_addr); |
| |
| val |= (u32)(chip->ci * 4) & 0x00FFFFFF; |
| /* Hardware Auto Response */ |
| val |= 0x40000000; |
| rtsx_writel(chip, RTSX_HCBCTLR, val); |
| |
| spin_unlock_irq(&rtsx->reg_lock); |
| |
| /* Wait for TRANS_OK_INT */ |
| timeleft = wait_for_completion_interruptible_timeout( |
| &trans_done, msecs_to_jiffies(timeout)); |
| if (timeleft <= 0) { |
| dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n", |
| chip->int_reg); |
| err = -ETIMEDOUT; |
| goto finish_send_cmd; |
| } |
| |
| spin_lock_irq(&rtsx->reg_lock); |
| if (rtsx->trans_result == TRANS_RESULT_FAIL) |
| err = -EIO; |
| else if (rtsx->trans_result == TRANS_RESULT_OK) |
| err = 0; |
| |
| spin_unlock_irq(&rtsx->reg_lock); |
| |
| finish_send_cmd: |
| rtsx->done = NULL; |
| rtsx->trans_state = STATE_TRANS_NONE; |
| |
| if (err < 0) |
| rtsx_stop_cmd(chip, card); |
| |
| return err; |
| } |
| |
| static inline void rtsx_add_sg_tbl( |
| struct rtsx_chip *chip, u32 addr, u32 len, u8 option) |
| { |
| __le64 *sgb = (__le64 *)(chip->host_sg_tbl_ptr); |
| u64 val = 0; |
| u32 temp_len = 0; |
| u8 temp_opt = 0; |
| |
| do { |
| if (len > 0x80000) { |
| temp_len = 0x80000; |
| temp_opt = option & (~RTSX_SG_END); |
| } else { |
| temp_len = len; |
| temp_opt = option; |
| } |
| val = ((u64)addr << 32) | ((u64)temp_len << 12) | temp_opt; |
| |
| if (chip->sgi < (HOST_SG_TBL_BUF_LEN / 8)) |
| sgb[(chip->sgi)++] = cpu_to_le64(val); |
| |
| len -= temp_len; |
| addr += temp_len; |
| } while (len); |
| } |
| |
| static int rtsx_transfer_sglist_adma_partial(struct rtsx_chip *chip, u8 card, |
| struct scatterlist *sg, int num_sg, |
| unsigned int *index, |
| unsigned int *offset, int size, |
| enum dma_data_direction dma_dir, |
| int timeout) |
| { |
| struct rtsx_dev *rtsx = chip->rtsx; |
| struct completion trans_done; |
| u8 dir; |
| int sg_cnt, i, resid; |
| int err = 0; |
| long timeleft; |
| struct scatterlist *sg_ptr; |
| u32 val = TRIG_DMA; |
| |
| if (!sg || (num_sg <= 0) || !offset || !index) |
| return -EIO; |
| |
| if (dma_dir == DMA_TO_DEVICE) |
| dir = HOST_TO_DEVICE; |
| else if (dma_dir == DMA_FROM_DEVICE) |
| dir = DEVICE_TO_HOST; |
| else |
| return -ENXIO; |
| |
| if (card == SD_CARD) |
| rtsx->check_card_cd = SD_EXIST; |
| else if (card == MS_CARD) |
| rtsx->check_card_cd = MS_EXIST; |
| else if (card == XD_CARD) |
| rtsx->check_card_cd = XD_EXIST; |
| else |
| rtsx->check_card_cd = 0; |
| |
| spin_lock_irq(&rtsx->reg_lock); |
| |
| /* set up data structures for the wakeup system */ |
| rtsx->done = &trans_done; |
| |
| rtsx->trans_state = STATE_TRANS_SG; |
| rtsx->trans_result = TRANS_NOT_READY; |
| |
| spin_unlock_irq(&rtsx->reg_lock); |
| |
| sg_cnt = dma_map_sg(&rtsx->pci->dev, sg, num_sg, dma_dir); |
| |
| resid = size; |
| sg_ptr = sg; |
| chip->sgi = 0; |
| /* |
| * Usually the next entry will be @sg@ + 1, but if this sg element |
| * is part of a chained scatterlist, it could jump to the start of |
| * a new scatterlist array. So here we use sg_next to move to |
| * the proper sg. |
| */ |
| for (i = 0; i < *index; i++) |
| sg_ptr = sg_next(sg_ptr); |
| for (i = *index; i < sg_cnt; i++) { |
| dma_addr_t addr; |
| unsigned int len; |
| u8 option; |
| |
| addr = sg_dma_address(sg_ptr); |
| len = sg_dma_len(sg_ptr); |
| |
| dev_dbg(rtsx_dev(chip), "DMA addr: 0x%x, Len: 0x%x\n", |
| (unsigned int)addr, len); |
| dev_dbg(rtsx_dev(chip), "*index = %d, *offset = %d\n", |
| *index, *offset); |
| |
| addr += *offset; |
| |
| if ((len - *offset) > resid) { |
| *offset += resid; |
| len = resid; |
| resid = 0; |
| } else { |
| resid -= (len - *offset); |
| len -= *offset; |
| *offset = 0; |
| *index = *index + 1; |
| } |
| if ((i == (sg_cnt - 1)) || !resid) |
| option = RTSX_SG_VALID | RTSX_SG_END | RTSX_SG_TRANS_DATA; |
| else |
| option = RTSX_SG_VALID | RTSX_SG_TRANS_DATA; |
| |
| rtsx_add_sg_tbl(chip, (u32)addr, (u32)len, option); |
| |
| if (!resid) |
| break; |
| |
| sg_ptr = sg_next(sg_ptr); |
| } |
| |
| dev_dbg(rtsx_dev(chip), "SG table count = %d\n", chip->sgi); |
| |
| val |= (u32)(dir & 0x01) << 29; |
| val |= ADMA_MODE; |
| |
| spin_lock_irq(&rtsx->reg_lock); |
| |
| init_completion(&trans_done); |
| |
| rtsx_writel(chip, RTSX_HDBAR, chip->host_sg_tbl_addr); |
| rtsx_writel(chip, RTSX_HDBCTLR, val); |
| |
| spin_unlock_irq(&rtsx->reg_lock); |
| |
| timeleft = wait_for_completion_interruptible_timeout( |
| &trans_done, msecs_to_jiffies(timeout)); |
| if (timeleft <= 0) { |
| dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n", |
| __func__, __LINE__); |
| dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n", |
| chip->int_reg); |
| err = -ETIMEDOUT; |
| goto out; |
| } |
| |
| spin_lock_irq(&rtsx->reg_lock); |
| if (rtsx->trans_result == TRANS_RESULT_FAIL) { |
| err = -EIO; |
| spin_unlock_irq(&rtsx->reg_lock); |
| goto out; |
| } |
| spin_unlock_irq(&rtsx->reg_lock); |
| |
| /* Wait for TRANS_OK_INT */ |
| spin_lock_irq(&rtsx->reg_lock); |
| if (rtsx->trans_result == TRANS_NOT_READY) { |
| init_completion(&trans_done); |
| spin_unlock_irq(&rtsx->reg_lock); |
| timeleft = wait_for_completion_interruptible_timeout( |
| &trans_done, msecs_to_jiffies(timeout)); |
| if (timeleft <= 0) { |
| dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n", |
| __func__, __LINE__); |
| dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n", |
| chip->int_reg); |
| err = -ETIMEDOUT; |
| goto out; |
| } |
| } else { |
| spin_unlock_irq(&rtsx->reg_lock); |
| } |
| |
| spin_lock_irq(&rtsx->reg_lock); |
| if (rtsx->trans_result == TRANS_RESULT_FAIL) |
| err = -EIO; |
| else if (rtsx->trans_result == TRANS_RESULT_OK) |
| err = 0; |
| |
| spin_unlock_irq(&rtsx->reg_lock); |
| |
| out: |
| rtsx->done = NULL; |
| rtsx->trans_state = STATE_TRANS_NONE; |
| dma_unmap_sg(&rtsx->pci->dev, sg, num_sg, dma_dir); |
| |
| if (err < 0) |
| rtsx_stop_cmd(chip, card); |
| |
| return err; |
| } |
| |
| static int rtsx_transfer_sglist_adma(struct rtsx_chip *chip, u8 card, |
| struct scatterlist *sg, int num_sg, |
| enum dma_data_direction dma_dir, |
| int timeout) |
| { |
| struct rtsx_dev *rtsx = chip->rtsx; |
| struct completion trans_done; |
| u8 dir; |
| int buf_cnt, i; |
| int err = 0; |
| long timeleft; |
| struct scatterlist *sg_ptr; |
| |
| if (!sg || (num_sg <= 0)) |
| return -EIO; |
| |
| if (dma_dir == DMA_TO_DEVICE) |
| dir = HOST_TO_DEVICE; |
| else if (dma_dir == DMA_FROM_DEVICE) |
| dir = DEVICE_TO_HOST; |
| else |
| return -ENXIO; |
| |
| if (card == SD_CARD) |
| rtsx->check_card_cd = SD_EXIST; |
| else if (card == MS_CARD) |
| rtsx->check_card_cd = MS_EXIST; |
| else if (card == XD_CARD) |
| rtsx->check_card_cd = XD_EXIST; |
| else |
| rtsx->check_card_cd = 0; |
| |
| spin_lock_irq(&rtsx->reg_lock); |
| |
| /* set up data structures for the wakeup system */ |
| rtsx->done = &trans_done; |
| |
| rtsx->trans_state = STATE_TRANS_SG; |
| rtsx->trans_result = TRANS_NOT_READY; |
| |
| spin_unlock_irq(&rtsx->reg_lock); |
| |
| buf_cnt = dma_map_sg(&rtsx->pci->dev, sg, num_sg, dma_dir); |
| |
| sg_ptr = sg; |
| |
| for (i = 0; i <= buf_cnt / (HOST_SG_TBL_BUF_LEN / 8); i++) { |
| u32 val = TRIG_DMA; |
| int sg_cnt, j; |
| |
| if (i == buf_cnt / (HOST_SG_TBL_BUF_LEN / 8)) |
| sg_cnt = buf_cnt % (HOST_SG_TBL_BUF_LEN / 8); |
| else |
| sg_cnt = HOST_SG_TBL_BUF_LEN / 8; |
| |
| chip->sgi = 0; |
| for (j = 0; j < sg_cnt; j++) { |
| dma_addr_t addr = sg_dma_address(sg_ptr); |
| unsigned int len = sg_dma_len(sg_ptr); |
| u8 option; |
| |
| dev_dbg(rtsx_dev(chip), "DMA addr: 0x%x, Len: 0x%x\n", |
| (unsigned int)addr, len); |
| |
| if (j == (sg_cnt - 1)) |
| option = RTSX_SG_VALID | RTSX_SG_END | RTSX_SG_TRANS_DATA; |
| else |
| option = RTSX_SG_VALID | RTSX_SG_TRANS_DATA; |
| |
| rtsx_add_sg_tbl(chip, (u32)addr, (u32)len, option); |
| |
| sg_ptr = sg_next(sg_ptr); |
| } |
| |
| dev_dbg(rtsx_dev(chip), "SG table count = %d\n", chip->sgi); |
| |
| val |= (u32)(dir & 0x01) << 29; |
| val |= ADMA_MODE; |
| |
| spin_lock_irq(&rtsx->reg_lock); |
| |
| init_completion(&trans_done); |
| |
| rtsx_writel(chip, RTSX_HDBAR, chip->host_sg_tbl_addr); |
| rtsx_writel(chip, RTSX_HDBCTLR, val); |
| |
| spin_unlock_irq(&rtsx->reg_lock); |
| |
| timeleft = wait_for_completion_interruptible_timeout( |
| &trans_done, msecs_to_jiffies(timeout)); |
| if (timeleft <= 0) { |
| dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n", |
| __func__, __LINE__); |
| dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n", |
| chip->int_reg); |
| err = -ETIMEDOUT; |
| goto out; |
| } |
| |
| spin_lock_irq(&rtsx->reg_lock); |
| if (rtsx->trans_result == TRANS_RESULT_FAIL) { |
| err = -EIO; |
| spin_unlock_irq(&rtsx->reg_lock); |
| goto out; |
| } |
| spin_unlock_irq(&rtsx->reg_lock); |
| |
| sg_ptr += sg_cnt; |
| } |
| |
| /* Wait for TRANS_OK_INT */ |
| spin_lock_irq(&rtsx->reg_lock); |
| if (rtsx->trans_result == TRANS_NOT_READY) { |
| init_completion(&trans_done); |
| spin_unlock_irq(&rtsx->reg_lock); |
| timeleft = wait_for_completion_interruptible_timeout( |
| &trans_done, msecs_to_jiffies(timeout)); |
| if (timeleft <= 0) { |
| dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n", |
| __func__, __LINE__); |
| dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n", |
| chip->int_reg); |
| err = -ETIMEDOUT; |
| goto out; |
| } |
| } else { |
| spin_unlock_irq(&rtsx->reg_lock); |
| } |
| |
| spin_lock_irq(&rtsx->reg_lock); |
| if (rtsx->trans_result == TRANS_RESULT_FAIL) |
| err = -EIO; |
| else if (rtsx->trans_result == TRANS_RESULT_OK) |
| err = 0; |
| |
| spin_unlock_irq(&rtsx->reg_lock); |
| |
| out: |
| rtsx->done = NULL; |
| rtsx->trans_state = STATE_TRANS_NONE; |
| dma_unmap_sg(&rtsx->pci->dev, sg, num_sg, dma_dir); |
| |
| if (err < 0) |
| rtsx_stop_cmd(chip, card); |
| |
| return err; |
| } |
| |
| static int rtsx_transfer_buf(struct rtsx_chip *chip, u8 card, void *buf, |
| size_t len, enum dma_data_direction dma_dir, |
| int timeout) |
| { |
| struct rtsx_dev *rtsx = chip->rtsx; |
| struct completion trans_done; |
| dma_addr_t addr; |
| u8 dir; |
| int err = 0; |
| u32 val = BIT(31); |
| long timeleft; |
| |
| if (!buf || (len <= 0)) |
| return -EIO; |
| |
| if (dma_dir == DMA_TO_DEVICE) |
| dir = HOST_TO_DEVICE; |
| else if (dma_dir == DMA_FROM_DEVICE) |
| dir = DEVICE_TO_HOST; |
| else |
| return -ENXIO; |
| |
| addr = dma_map_single(&rtsx->pci->dev, buf, len, dma_dir); |
| if (dma_mapping_error(&rtsx->pci->dev, addr)) |
| return -ENOMEM; |
| |
| if (card == SD_CARD) |
| rtsx->check_card_cd = SD_EXIST; |
| else if (card == MS_CARD) |
| rtsx->check_card_cd = MS_EXIST; |
| else if (card == XD_CARD) |
| rtsx->check_card_cd = XD_EXIST; |
| else |
| rtsx->check_card_cd = 0; |
| |
| val |= (u32)(dir & 0x01) << 29; |
| val |= (u32)(len & 0x00FFFFFF); |
| |
| spin_lock_irq(&rtsx->reg_lock); |
| |
| /* set up data structures for the wakeup system */ |
| rtsx->done = &trans_done; |
| |
| init_completion(&trans_done); |
| |
| rtsx->trans_state = STATE_TRANS_BUF; |
| rtsx->trans_result = TRANS_NOT_READY; |
| |
| rtsx_writel(chip, RTSX_HDBAR, addr); |
| rtsx_writel(chip, RTSX_HDBCTLR, val); |
| |
| spin_unlock_irq(&rtsx->reg_lock); |
| |
| /* Wait for TRANS_OK_INT */ |
| timeleft = wait_for_completion_interruptible_timeout( |
| &trans_done, msecs_to_jiffies(timeout)); |
| if (timeleft <= 0) { |
| dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n", |
| __func__, __LINE__); |
| dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n", |
| chip->int_reg); |
| err = -ETIMEDOUT; |
| goto out; |
| } |
| |
| spin_lock_irq(&rtsx->reg_lock); |
| if (rtsx->trans_result == TRANS_RESULT_FAIL) |
| err = -EIO; |
| else if (rtsx->trans_result == TRANS_RESULT_OK) |
| err = 0; |
| |
| spin_unlock_irq(&rtsx->reg_lock); |
| |
| out: |
| rtsx->done = NULL; |
| rtsx->trans_state = STATE_TRANS_NONE; |
| dma_unmap_single(&rtsx->pci->dev, addr, len, dma_dir); |
| |
| if (err < 0) |
| rtsx_stop_cmd(chip, card); |
| |
| return err; |
| } |
| |
| int rtsx_transfer_data_partial(struct rtsx_chip *chip, u8 card, |
| void *buf, size_t len, int use_sg, |
| unsigned int *index, unsigned int *offset, |
| enum dma_data_direction dma_dir, int timeout) |
| { |
| int err = 0; |
| |
| /* don't transfer data during abort processing */ |
| if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) |
| return -EIO; |
| |
| if (use_sg) { |
| struct scatterlist *sg = buf; |
| |
| err = rtsx_transfer_sglist_adma_partial(chip, card, sg, use_sg, |
| index, offset, (int)len, |
| dma_dir, timeout); |
| } else { |
| err = rtsx_transfer_buf(chip, card, |
| buf, len, dma_dir, timeout); |
| } |
| if (err < 0) { |
| if (RTSX_TST_DELINK(chip)) { |
| RTSX_CLR_DELINK(chip); |
| chip->need_reinit = SD_CARD | MS_CARD | XD_CARD; |
| rtsx_reinit_cards(chip, 1); |
| } |
| } |
| |
| return err; |
| } |
| |
| int rtsx_transfer_data(struct rtsx_chip *chip, u8 card, void *buf, size_t len, |
| int use_sg, enum dma_data_direction dma_dir, int timeout) |
| { |
| int err = 0; |
| |
| dev_dbg(rtsx_dev(chip), "use_sg = %d\n", use_sg); |
| |
| /* don't transfer data during abort processing */ |
| if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) |
| return -EIO; |
| |
| if (use_sg) { |
| err = rtsx_transfer_sglist_adma(chip, card, buf, |
| use_sg, dma_dir, timeout); |
| } else { |
| err = rtsx_transfer_buf(chip, card, buf, len, dma_dir, timeout); |
| } |
| |
| if (err < 0) { |
| if (RTSX_TST_DELINK(chip)) { |
| RTSX_CLR_DELINK(chip); |
| chip->need_reinit = SD_CARD | MS_CARD | XD_CARD; |
| rtsx_reinit_cards(chip, 1); |
| } |
| } |
| |
| return err; |
| } |
| |