static int mmc_ext_csd_open(struct inode *inode, struct file *filp) { struct mmc_card *card = inode->i_private; char *buf; ssize_t n = 0; u8 *ext_csd; int err, i; buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL); if (!buf) return -ENOMEM; mmc_get_card(card); err = mmc_get_ext_csd(card, &ext_csd); mmc_put_card(card); if (err) goto out_free; for (i = 0; i < 512; i++) n += sprintf(buf + n, "%02x", ext_csd[i]); n += sprintf(buf + n, "\n"); BUG_ON(n != EXT_CSD_STR_LEN); filp->private_data = buf; kfree(ext_csd); return 0; out_free: kfree(buf); return err; }
static void mmc_mq_recovery_handler(struct work_struct *work) { struct mmc_queue *mq = container_of(work, struct mmc_queue, recovery_work); struct request_queue *q = mq->queue; mmc_get_card(mq->card, &mq->ctx); mq->in_recovery = true; if (mq->use_cqe) mmc_blk_cqe_recovery(mq); else mmc_blk_mq_recovery(mq); mq->in_recovery = false; spin_lock_irq(&mq->lock); mq->recovery_needed = false; spin_unlock_irq(&mq->lock); mmc_put_card(mq->card, &mq->ctx); blk_mq_run_hw_queues(q, true); }
int mmc_erase_blks(int dev_id, u32 addr, u32 size, int bootarea) { unsigned long ret; int i, j, result = 0; u8 val; u8 *ext_csd; u32 blknr; u32 total_blks; struct mmc_card *card; if (!size) return 0; if (addr % MMC_BLOCK_SIZE) return MMC_ERR_FAILED; card = mmc_get_card(dev_id); ext_csd = &card->raw_ext_csd[0]; if (bootarea && !mmc_card_sd(card) && card->ext_csd.part_en) { /* configure to specified partition */ val = (ext_csd[EXT_CSD_PART_CFG] & ~0x7) | EXT_CSD_PART_CFG_BOOT_PART_1; if (mmc_set_part_config(card, val) != MMC_ERR_NONE) { result = -__LINE__; goto done; } } blknr = addr / MMC_BLOCK_SIZE; total_blks = (size + MMC_BLOCK_SIZE - 1) / MMC_BLOCK_SIZE; if (mmc_erase_start(card, blknr * MMC_BLOCK_SIZE) != MMC_ERR_NONE) { result = -__LINE__; goto done; } if (mmc_erase_end(card, (blknr + total_blks) * MMC_BLOCK_SIZE) != MMC_ERR_NONE) { result = -__LINE__; goto done; } if (mmc_erase(card, MMC_ERASE_NORMAL) != MMC_ERR_NONE) { result = -__LINE__; goto done; } done: if (bootarea && !mmc_card_sd(card) && card->ext_csd.part_en) { /* configure to user partition */ val = (ext_csd[EXT_CSD_PART_CFG] & ~0x7) | EXT_CSD_PART_CFG_DEFT_PART; if (mmc_set_part_config(card, val) != MMC_ERR_NONE) result = -__LINE__; } if (!result) { printf("[SD%d] Erase %d blocks (%d bytes) from 0x%x successfully\n", dev_id, total_blks, total_blks * MMC_BLOCK_SIZE, blknr * MMC_BLOCK_SIZE); } else { printf("[SD%d] Erase %d blocks (%d bytes) from 0x%x failed %d\n", dev_id, total_blks, total_blks * MMC_BLOCK_SIZE, blknr * MMC_BLOCK_SIZE, result); } return result; }
int mmc_readback_blks(int dev_id, unsigned long addr, int blks, int bootarea) { int i, j, result = 0; u8 val; u8 *ext_csd; unsigned long blknr = addr / MMC_BLOCK_SIZE; unsigned char *buf = (unsigned char*)MMC_BUF_ADDR; struct mmc_card *card; struct mmc_host *host; host = mmc_get_host(dev_id); card = mmc_get_card(dev_id); ext_csd = &card->raw_ext_csd[0]; if (bootarea && !mmc_card_sd(card)) { /* configure to specified partition */ val = (ext_csd[EXT_CSD_PART_CFG] & ~0x7) | EXT_CSD_PART_CFG_BOOT_PART_1; if (mmc_set_part_config(card, val) != MMC_ERR_NONE) { result = -__LINE__; goto done; } if (mmc_read_ext_csd(host, card) != MMC_ERR_NONE) { result = -__LINE__; goto done; } } printf("[SD%d] Dump %d blks from 0x%x (FLASH)\n", dev_id, blks, blknr * MMC_BLOCK_SIZE); for (i = 0; i < blks; i++) { memset(buf, 0, MMC_BLOCK_SIZE); if (MMC_ERR_NONE != mmc_block_read(dev_id, blknr + i, 1, (unsigned long*)buf)) { printf("\n[SD%d] Read from %dth block error\n", dev_id, blknr + i); break; } for (j = 0; j < MMC_BLOCK_SIZE; j++) { if (j % 16 == 0) printf("\n%xh: ", (blknr + i) * MMC_BLOCK_SIZE + j); printf("%x ", buf[j]); } printf("\n"); buf += MMC_BLOCK_SIZE; } done: if (bootarea && !mmc_card_sd(card)) { /* configure to user partition */ val = (ext_csd[EXT_CSD_PART_CFG] & ~0x7) | EXT_CSD_PART_CFG_DEFT_PART; if (mmc_set_part_config(card, val) != MMC_ERR_NONE) result = -__LINE__; if (mmc_read_ext_csd(host, card) != MMC_ERR_NONE) { result = -__LINE__; } } return result; }
int mmc_boot_up_test(int id, int reset, u32 freq, int in_idle) { int err = MMC_ERR_FAILED; struct mmc_host *host; struct mmc_card *card; host = mmc_get_host(id); card = mmc_get_card(id); mmc_init_host(host, id); if (in_idle) { printf("[EMMC] Card is in idle mode \n"); /* card in idle mode */ //mmc_go_idle(host); mmc_init_card(host, card); /* note: requires delay before issue boot reset command */ mdelay(5); } else { /* card not in idle mode */ printf("[EMMC] Card is not in idle mode \n"); } msdc_emmc_boot_reset(host, reset); err = msdc_emmc_boot_start(host, freq, 0, EMMC_BOOT_RST_CMD_MODE, 0); if (err) { printf("[EMMC] Boot Error: %d\n", err); goto done; } err = msdc_emmc_boot_read(host, 128 * 1024, MMC_BUF_ADDR); msdc_emmc_boot_stop(host, EMMC_BOOT_RST_CMD_MODE); if (err) { printf("[EMMC] Boot Read Error: %d\n", err); goto done; } done: if (!err) { int i, j; char *buf = (char*)MMC_BUF_ADDR; for (i = 0; i < 16; i++) { for (j = 0; j < MMC_BLOCK_SIZE; j++) { if (j % 16 == 0) printf("\n%xh: ", i * MMC_BLOCK_SIZE + j); printf("%x ", buf[j]); } printf("\n"); buf += MMC_BLOCK_SIZE; } } return err; }
static int mmc_dbg_card_status_get(void *data, u64 *val) { struct mmc_card *card = data; u32 status; int ret; mmc_get_card(card); ret = mmc_send_status(data, &status); if (!ret) *val = status; mmc_put_card(card); return ret; }
int mmc_boot_enable(int id, int bootpart) { int err = MMC_ERR_FAILED; struct mmc_host *host; struct mmc_card *card; host = mmc_get_host(id); card = mmc_get_card(id); err = mmc_boot_config(card, EXT_CSD_PART_CFG_EN_ACK, bootpart, EXT_CSD_BOOT_BUS_WIDTH_1, EXT_CSD_BOOT_BUS_MODE_DEFT); if (err != 0) goto done; err = mmc_read_ext_csd(host, card); done: return err; }
int mmc_download_part(int dev_id, char *part_name, int bootarea) { int ret = -1; struct mmc_card *card; struct mmc_host *host; part_t *part = mt6573_part_get_partition(part_name); mmc_download_addr = 0; mmc_download_size = 0; mmc_image_addr = 0; host = mmc_get_host(dev_id); card = mmc_get_card(dev_id); if (part) { printf("[SD%d] Waiting for '%s' image loading from ICE...\n", dev_id, part_name); while (!mmc_download_size); /* Wait for loading image from ICE */ ret = mmc_download(dev_id, mmc_image_addr, mmc_download_size, part->startblk * BLK_SIZE, bootarea); if (ret != 0) goto done; if (bootarea) { /* set reset signal function */ //ret = mmc_set_reset_func(card, 1); //if (ret != 0) // goto done; /* set boot config */ ret = mmc_boot_config(card, EXT_CSD_PART_CFG_EN_ACK, EXT_CSD_PART_CFG_EN_BOOT_PART_1, EXT_CSD_BOOT_BUS_WIDTH_1, EXT_CSD_BOOT_BUS_MODE_DEFT); if (ret != 0) goto done; ret = mmc_read_ext_csd(host, card); } } done: return ret; }
int mmc_test_mem_card(struct mmc_test_config *cfg) { int id, count, forever; int ret, chk_result, tid = 0, result = 0; unsigned int chunks, chunk_blks, left_blks, pass = 0, fail = 0; unsigned int total_blks; unsigned int i, j; unsigned int blksz; unsigned int clkhz; char pattern = 0; char *buf; unsigned long blknr; struct mmc_host *host; struct mmc_card *card; id = cfg->id; count = cfg->count; buf = cfg->buf; blknr = cfg->blknr; blksz = cfg->blksz; chk_result = cfg->chk_result; chunk_blks = cfg->chunk_blks; total_blks = (cfg->total_size + blksz - 1) / blksz; forever = (count == -1) ? 1 : 0; host = mmc_get_host(id); card = mmc_get_card(id); while (forever || count--) { printf("[TST] ==============================================\n"); printf("[TST] BEGIN: %d/%d, No Stop(%d)\n", (cfg->count != -1) ? cfg->count - count : 0, (cfg->count != -1) ? cfg->count : 0, forever); printf("[TST] ----------------------------------------------\n"); printf("[TST] Mode : %d\n", cfg->mode); printf("[TST] Clock : %d kHz\n", cfg->clock / 1000); printf("[TST] BusWidth: %d bits\n", cfg->buswidth); printf("[TST] BurstSz : %d bytes\n", 0x1 << cfg->burstsz); printf("[TST] BlkAddr : %xh\n", blknr); printf("[TST] BlkSize : %dbytes\n", blksz); printf("[TST] TstBlks : %d\n", total_blks); #if defined(BB_MT6575) printf("[TST] AutoCMD : 12(%d), 23(%d)\n", (cfg->autocmd & MSDC_AUTOCMD12) ? 1 : 0, (cfg->autocmd & MSDC_AUTOCMD23) ? 1 : 0); #endif printf("[TST] ----------------------------------------------\n"); if (mmc_init_host(host, id) != 0) { result = -__LINE__; goto failure; } if (mmc_init_card(host, card) != 0) { result = -__LINE__; goto failure; } #if defined(BB_MT6575) msdc_set_dma(host, (u8)cfg->burstsz, (u32)cfg->flags); msdc_set_autocmd(host, cfg->autocmd, 1); #endif /* change uhs-1 mode */ #if 0 if (mmc_card_uhs1(card)) { if (mmc_switch_uhs1(host, card, cfg->uhsmode) != 0) { result = -__LINE__; goto failure; } } #endif /* change clock */ if (cfg->clock) { clkhz = card->maxhz < cfg->clock ? card->maxhz : cfg->clock; mmc_set_clock(host, mmc_card_ddr(card), clkhz); } if (mmc_card_sd(card) && cfg->buswidth == HOST_BUS_WIDTH_8) { printf("[TST] SD card doesn't support 8-bit bus width (SKIP)\n"); result = MMC_ERR_NONE; } if (mmc_set_bus_width(host, card, cfg->buswidth) != 0) { result = -__LINE__; goto failure; } /* cmd16 is illegal while card is in ddr mode */ if (!(mmc_card_mmc(card) && mmc_card_ddr(card))) { if (mmc_set_blk_length(host, blksz) != 0) { result = -__LINE__; goto failure; } } #if defined(BB_MT6575) if (cfg->piobits) { printf("[TST] PIO bits: %d\n", cfg->piobits); msdc_set_pio_bits(host, cfg->piobits); } #endif tid = result = 0; if (mmc_erase_start(card, blknr * blksz) != MMC_ERR_NONE) { result = -__LINE__; goto failure; } if (mmc_erase_end(card, (blknr + total_blks) * blksz) != MMC_ERR_NONE) { result = -__LINE__; goto failure; } if (mmc_erase(card, MMC_ERASE_NORMAL) != MMC_ERR_NONE) { result = -__LINE__; goto failure; } printf("[TST] 0x%x - 0x%x Erased\n", blknr * blksz, (blknr + total_blks) * blksz); mmc_send_status(host, card, &status); if (cfg->tst_single) { /* single block write */ for (i = 0; i < total_blks; i++) { pattern = (i + count) % 256; memset(buf, pattern, blksz); ret = mmc_block_write(id, blknr + i, 1, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { printf("test single block write failed (%d)\n", i); result = -__LINE__; goto failure; } } printf(TC_MSG, host->id, result == 0 ? "PASS" : "FAIL", tid++, "test single block write\n"); if (result) break; /* single block read */ for (i = 0; i < total_blks && !result; i++) { pattern = (i + count) % 256; /* populate buffer with different pattern */ memset(buf, pattern + 1, blksz); ret = mmc_block_read(id, blknr + i, 1, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto failure; } if (chk_result) { for (j = 0; j < blksz; j++) { if (buf[j] != pattern) { result = -__LINE__; goto failure; } } } } printf(TC_MSG, host->id, result == 0 ? "PASS" : "FAIL", tid++, "test single block read\n"); if (result) { printf("[SD%d]\t\tread back pattern(0x%.2x) failed\n", id, pattern); goto failure; } } mmc_send_status(host, card, &status); if (cfg->tst_multiple) { /* multiple block write */ chunks = total_blks / chunk_blks; left_blks = total_blks % chunk_blks; for (i = 0; i < chunks; i++) { pattern = (i + count) % 256; memset(buf, pattern, blksz * chunk_blks); ret = mmc_block_write(id, blknr + i * chunk_blks, chunk_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto failure; } } if (!result && left_blks) { pattern = (i + count) % 256; memset(buf, pattern, blksz * left_blks); ret = mmc_block_write(id, blknr + chunks * chunk_blks, left_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto failure; } } printf(TC_MSG, host->id, result == 0 ? "PASS" : "FAIL", tid++, "test multiple block write\n"); if (result) goto failure; /* multiple block read */ for (i = 0; i < chunks; i++) { pattern = (i + count) % 256; /* populate buffer with different pattern */ memset(buf, pattern + 1, blksz); ret = mmc_block_read(id, blknr + i * chunk_blks, chunk_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { printf("[SD%d]\t\tread %d blks failed(ret = %d blks)\n", host->id, chunk_blks, ret); result = -__LINE__; goto failure; } if (chk_result) { for (j = 0; j < chunk_blks * blksz; j++) { if (buf[j] == pattern) continue; result = -__LINE__; printf("[SD%d]\t\t%xh = %x (!= %x)\n", host->id, blknr + i * chunk_blks + j, buf[j], pattern); goto failure; } } } if (!result && left_blks) { pattern = i % 256; /* populate buffer with different pattern */ memset(buf, pattern + 1, blksz); ret = mmc_block_read(id, blknr + chunks * chunk_blks, left_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { printf("[SD%d]\t\tread %d blks failed(ret = %d blks)\n", host->id, left_blks, ret); result = -__LINE__; goto failure; } if (chk_result) { for (j = 0; j < left_blks * blksz; j++) { if (buf[j] == pattern) continue; printf("[SD%d]\t\t%xh = %x (!= %x)\n", host->id, blknr + chunks * chunk_blks + j, buf[j], pattern); result = -__LINE__; goto failure; } } } printf(TC_MSG, host->id, result == 0 ? "PASS" : "FAIL", tid++, "test multiple block read\n"); if (result) goto failure; } mmc_send_status(host, card, &status); if (cfg->tst_interleave) { /* multiple block write */ chunks = total_blks / chunk_blks; left_blks = total_blks % chunk_blks; for (i = 0; i < chunks; i++) { pattern = (i + count) % 256; memset(buf, pattern, blksz * chunk_blks); ret = mmc_block_write(id, blknr + i * chunk_blks, chunk_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto failure; } /* populate buffer with different pattern */ memset(buf, pattern + 1, blksz * chunk_blks); ret = mmc_block_read(id, blknr + i * chunk_blks, chunk_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto failure; } if (chk_result) { for (j = 0; j < chunk_blks * blksz; j++) { if (buf[j] == pattern) continue; result = -__LINE__; goto failure; } } } if (!result && left_blks) { pattern = (i + count) % 256; memset(buf, pattern, blksz * left_blks); ret = mmc_block_write(id, blknr + chunks * chunk_blks, left_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto failure; } /* populate buffer with different pattern */ memset(buf, pattern + 1, blksz * left_blks); ret = mmc_block_read(id, blknr + chunks * chunk_blks, left_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; break; } if (chk_result) { for (j = 0; j < left_blks * blksz; j++) { if (buf[j] == pattern) continue; result = -__LINE__; goto failure; } } } printf(TC_MSG, host->id, result == 0 ? "PASS" : "FAIL", tid++, "test multiple block interleave write-read\n"); if (result) goto failure; } if (cfg->desc) { printf("[TST] ----------------------------------------------\n"); printf("[TST] Report - %s \n", cfg->desc); printf("[TST] ----------------------------------------------\n"); } mmc_prof_dump(id); failure: if (result) { printf("[SD%d] mmc test failed (%d)\n", host->id, result); fail++; } else { pass++; } printf("[TST] ----------------------------------------------\n"); printf("[TST] Test Result: TOTAL(%d/%d), PASS(%d), FAIL(%d) \n", cfg->count - count, cfg->count, pass, fail); printf("[TST] ----------------------------------------------\n"); //mdelay(1000); } return result; }
int mmc_download(int dev_id, u32 imgaddr, u32 size, u32 addr, int bootarea) { int ret; int i, j, result = 0; u8 val; u8 *ext_csd; uchar *buf, *chkbuf; u32 chunks, chunk_blks = 128, left_blks, blknr; u32 total_blks; struct mmc_card *card; if (!size) return 0; if (addr % MMC_BLOCK_SIZE) return MMC_ERR_FAILED; card = mmc_get_card(dev_id); ext_csd = &card->raw_ext_csd[0]; if (bootarea && !mmc_card_sd(card) && card->ext_csd.part_en) { /* configure to specified partition */ val = (ext_csd[EXT_CSD_PART_CFG] & ~0x7) | EXT_CSD_PART_CFG_BOOT_PART_1; if (mmc_set_part_config(card, val) != MMC_ERR_NONE) { result = -__LINE__; goto done; } } blknr = addr / MMC_BLOCK_SIZE; total_blks = (size + MMC_BLOCK_SIZE - 1) / MMC_BLOCK_SIZE; /* multiple block write */ chunks = total_blks / chunk_blks; left_blks = total_blks % chunk_blks; buf = (uchar*)imgaddr; chkbuf = (uchar*)MMC_BUF_ADDR; for (i = 0; i < chunks; i++) { ret = mmc_block_write(dev_id, blknr + i * chunk_blks, chunk_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto done; } ret = mmc_block_read(dev_id, blknr + i * chunk_blks, chunk_blks, (unsigned long*)chkbuf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto done; } for (j = 0; j < chunk_blks * MMC_BLOCK_SIZE; j++) { if (buf[j] == chkbuf[j]) continue; result = -__LINE__; goto done; } printf("[SD%d] Write %3d blocks from 0x%.8x(RAM) to 0x%.8x(FLASH).\n", dev_id, chunk_blks, (unsigned int)buf, (blknr + i * chunk_blks) * MMC_BLOCK_SIZE); buf += (chunk_blks * MMC_BLOCK_SIZE); } if (left_blks) { ret = mmc_block_write(dev_id, blknr + chunks * chunk_blks, left_blks, (unsigned long*)buf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto done; } ret = mmc_block_read(dev_id, blknr + chunks * chunk_blks, left_blks, (unsigned long*)chkbuf); if (ret != MMC_ERR_NONE) { result = -__LINE__; goto done; } for (j = 0; j < left_blks * MMC_BLOCK_SIZE; j++) { if (buf[j] == chkbuf[j]) continue; printf("[SD%d] chkbuf[%d] = %xh (!= %xh) \n", dev_id, j, chkbuf[j], buf[j]); result = -__LINE__; goto done; } printf("[SD%d] Write %3d blocks from 0x%.8x(RAM) to 0x%.8x(FLASH).\n", dev_id, left_blks, (unsigned int)buf, (blknr + chunks * chunk_blks) * MMC_BLOCK_SIZE); } done: if (bootarea && !mmc_card_sd(card) && card->ext_csd.part_en) { /* configure to user partition */ val = (ext_csd[EXT_CSD_PART_CFG] & ~0x7) | EXT_CSD_PART_CFG_DEFT_PART; if (mmc_set_part_config(card, val) != MMC_ERR_NONE) result = -__LINE__; } if (!result) { printf("[SD%d] Download %d blocks (%d bytes) to 0x%.8x successfully\n", dev_id, total_blks, total_blks * MMC_BLOCK_SIZE, blknr * MMC_BLOCK_SIZE); } else { printf("[SD%d] Download %d blocks (%d bytes) to 0x%.8x failed %d\n", dev_id, total_blks, total_blks * MMC_BLOCK_SIZE, blknr * MMC_BLOCK_SIZE, result); } return result; }
static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd) { struct request *req = bd->rq; struct request_queue *q = req->q; struct mmc_queue *mq = q->queuedata; struct mmc_card *card = mq->card; struct mmc_host *host = card->host; enum mmc_issue_type issue_type; enum mmc_issued issued; bool get_card, cqe_retune_ok; int ret; if (mmc_card_removed(mq->card)) { req->rq_flags |= RQF_QUIET; return BLK_STS_IOERR; } issue_type = mmc_issue_type(mq, req); spin_lock_irq(&mq->lock); if (mq->recovery_needed || mq->busy) { spin_unlock_irq(&mq->lock); return BLK_STS_RESOURCE; } switch (issue_type) { case MMC_ISSUE_DCMD: if (mmc_cqe_dcmd_busy(mq)) { mq->cqe_busy |= MMC_CQE_DCMD_BUSY; spin_unlock_irq(&mq->lock); return BLK_STS_RESOURCE; } break; case MMC_ISSUE_ASYNC: break; default: /* * Timeouts are handled by mmc core, and we don't have a host * API to abort requests, so we can't handle the timeout anyway. * However, when the timeout happens, blk_mq_complete_request() * no longer works (to stop the request disappearing under us). * To avoid racing with that, set a large timeout. */ req->timeout = 600 * HZ; break; } /* Parallel dispatch of requests is not supported at the moment */ mq->busy = true; mq->in_flight[issue_type] += 1; get_card = (mmc_tot_in_flight(mq) == 1); cqe_retune_ok = (mmc_cqe_qcnt(mq) == 1); spin_unlock_irq(&mq->lock); if (!(req->rq_flags & RQF_DONTPREP)) { req_to_mmc_queue_req(req)->retries = 0; req->rq_flags |= RQF_DONTPREP; } if (get_card) mmc_get_card(card, &mq->ctx); if (mq->use_cqe) { host->retune_now = host->need_retune && cqe_retune_ok && !host->hold_retune; } blk_mq_start_request(req); issued = mmc_blk_mq_issue_rq(mq, req); switch (issued) { case MMC_REQ_BUSY: ret = BLK_STS_RESOURCE; break; case MMC_REQ_FAILED_TO_START: ret = BLK_STS_IOERR; break; default: ret = BLK_STS_OK; break; } if (issued != MMC_REQ_STARTED) { bool put_card = false; spin_lock_irq(&mq->lock); mq->in_flight[issue_type] -= 1; if (mmc_tot_in_flight(mq) == 0) put_card = true; mq->busy = false; spin_unlock_irq(&mq->lock); if (put_card) mmc_put_card(card, &mq->ctx); } else { WRITE_ONCE(mq->busy, false); } return ret; }