diff options
Diffstat (limited to 'drivers/mmc/host/rtsx_pci_sdmmc.c')
-rw-r--r-- | drivers/mmc/host/rtsx_pci_sdmmc.c | 529 |
1 files changed, 382 insertions, 147 deletions
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index c46feda07d56..5fb994f9a653 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -31,14 +31,9 @@ #include <linux/mfd/rtsx_pci.h> #include <asm/unaligned.h> -/* SD Tuning Data Structure - * Record continuous timing phase path - */ -struct timing_phase_path { - int start; - int end; - int mid; - int len; +struct realtek_next { + unsigned int sg_count; + s32 cookie; }; struct realtek_pci_sdmmc { @@ -46,9 +41,18 @@ struct realtek_pci_sdmmc { struct rtsx_pcr *pcr; struct mmc_host *mmc; struct mmc_request *mrq; - - struct mutex host_mutex; - + struct mmc_command *cmd; + struct mmc_data *data; + + spinlock_t lock; + struct timer_list timer; + struct tasklet_struct cmd_tasklet; + struct tasklet_struct data_tasklet; + struct tasklet_struct finish_tasklet; + + u8 rsp_type; + u8 rsp_len; + int sg_count; u8 ssc_depth; unsigned int clock; bool vpclk; @@ -58,8 +62,13 @@ struct realtek_pci_sdmmc { int power_state; #define SDMMC_POWER_ON 1 #define SDMMC_POWER_OFF 0 + + struct realtek_next next_data; }; +static int sd_start_multi_rw(struct realtek_pci_sdmmc *host, + struct mmc_request *mrq); + static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host) { return &(host->pdev->dev); @@ -96,6 +105,95 @@ static void sd_print_debug_regs(struct realtek_pci_sdmmc *host) #define sd_print_debug_regs(host) #endif /* DEBUG */ +static void sd_isr_done_transfer(struct platform_device *pdev) +{ + struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + + spin_lock(&host->lock); + if (host->cmd) + tasklet_schedule(&host->cmd_tasklet); + if (host->data) + tasklet_schedule(&host->data_tasklet); + spin_unlock(&host->lock); +} + +static void sd_request_timeout(unsigned long host_addr) +{ + struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + if (!host->mrq) { + dev_err(sdmmc_dev(host), "error: no request exist\n"); + goto out; + } + + if (host->cmd) + host->cmd->error = -ETIMEDOUT; + if (host->data) + host->data->error = -ETIMEDOUT; + + dev_dbg(sdmmc_dev(host), "timeout for request\n"); + +out: + tasklet_schedule(&host->finish_tasklet); + spin_unlock_irqrestore(&host->lock, flags); +} + +static void sd_finish_request(unsigned long host_addr) +{ + struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr; + struct rtsx_pcr *pcr = host->pcr; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + unsigned long flags; + bool any_error; + + spin_lock_irqsave(&host->lock, flags); + + del_timer(&host->timer); + mrq = host->mrq; + if (!mrq) { + dev_err(sdmmc_dev(host), "error: no request need finish\n"); + goto out; + } + + cmd = mrq->cmd; + data = mrq->data; + + any_error = (mrq->sbc && mrq->sbc->error) || + (mrq->stop && mrq->stop->error) || + (cmd && cmd->error) || (data && data->error); + + if (any_error) { + rtsx_pci_stop_cmd(pcr); + sd_clear_error(host); + } + + if (data) { + if (any_error) + data->bytes_xfered = 0; + else + data->bytes_xfered = data->blocks * data->blksz; + + if (!data->host_cookie) + rtsx_pci_dma_unmap_sg(pcr, data->sg, data->sg_len, + data->flags & MMC_DATA_READ); + + } + + host->mrq = NULL; + host->cmd = NULL; + host->data = NULL; + +out: + spin_unlock_irqrestore(&host->lock, flags); + mutex_unlock(&pcr->pcr_mutex); + mmc_request_done(host->mmc, mrq); +} + static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, u8 *buf, int buf_len, int timeout) { @@ -213,8 +311,7 @@ static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, return 0; } -static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, - struct mmc_command *cmd) +static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd) { struct rtsx_pcr *pcr = host->pcr; u8 cmd_idx = (u8)cmd->opcode; @@ -222,11 +319,14 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, int err = 0; int timeout = 100; int i; - u8 *ptr; - int stat_idx = 0; u8 rsp_type; int rsp_len = 5; - bool clock_toggled = false; + unsigned long flags; + + if (host->cmd) + dev_err(sdmmc_dev(host), "error: cmd already exist\n"); + + host->cmd = cmd; dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", __func__, cmd_idx, arg); @@ -261,6 +361,8 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, err = -EINVAL; goto out; } + host->rsp_type = rsp_type; + host->rsp_len = rsp_len; if (rsp_type == SD_RSP_TYPE_R1b) timeout = 3000; @@ -270,8 +372,6 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, 0xFF, SD_CLK_TOGGLE_EN); if (err < 0) goto out; - - clock_toggled = true; } rtsx_pci_init_cmd(pcr); @@ -295,25 +395,60 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, /* Read data from ping-pong buffer */ for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++) rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); - stat_idx = 16; } else if (rsp_type != SD_RSP_TYPE_R0) { /* Read data from SD_CMDx registers */ for (i = SD_CMD0; i <= SD_CMD4; i++) rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); - stat_idx = 5; } rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0); - err = rtsx_pci_send_cmd(pcr, timeout); - if (err < 0) { - sd_print_debug_regs(host); - sd_clear_error(host); - dev_dbg(sdmmc_dev(host), - "rtsx_pci_send_cmd error (err = %d)\n", err); + mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout)); + + spin_lock_irqsave(&pcr->lock, flags); + pcr->trans_result = TRANS_NOT_READY; + rtsx_pci_send_cmd_no_wait(pcr); + spin_unlock_irqrestore(&pcr->lock, flags); + + return; + +out: + cmd->error = err; + tasklet_schedule(&host->finish_tasklet); +} + +static void sd_get_rsp(unsigned long host_addr) +{ + struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr; + struct rtsx_pcr *pcr = host->pcr; + struct mmc_command *cmd; + int i, err = 0, stat_idx; + u8 *ptr, rsp_type; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + cmd = host->cmd; + host->cmd = NULL; + + if (!cmd) { + dev_err(sdmmc_dev(host), "error: cmd not exist\n"); goto out; } + spin_lock(&pcr->lock); + if (pcr->trans_result == TRANS_NO_DEVICE) + err = -ENODEV; + else if (pcr->trans_result != TRANS_RESULT_OK) + err = -EINVAL; + spin_unlock(&pcr->lock); + + if (err < 0) + goto out; + + rsp_type = host->rsp_type; + stat_idx = host->rsp_len; + if (rsp_type == SD_RSP_TYPE_R0) { err = 0; goto out; @@ -350,26 +485,106 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, cmd->resp[0]); } + if (cmd == host->mrq->sbc) { + sd_send_cmd(host, host->mrq->cmd); + spin_unlock_irqrestore(&host->lock, flags); + return; + } + + if (cmd == host->mrq->stop) + goto out; + + if (cmd->data) { + sd_start_multi_rw(host, host->mrq); + spin_unlock_irqrestore(&host->lock, flags); + return; + } + out: cmd->error = err; - if (err && clock_toggled) - rtsx_pci_write_register(pcr, SD_BUS_STAT, - SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); + tasklet_schedule(&host->finish_tasklet); + spin_unlock_irqrestore(&host->lock, flags); } -static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) +static int sd_pre_dma_transfer(struct realtek_pci_sdmmc *host, + struct mmc_data *data, struct realtek_next *next) +{ + struct rtsx_pcr *pcr = host->pcr; + int read = data->flags & MMC_DATA_READ; + int sg_count = 0; + + if (!next && data->host_cookie && + data->host_cookie != host->next_data.cookie) { + dev_err(sdmmc_dev(host), + "error: invalid cookie data[%d] host[%d]\n", + data->host_cookie, host->next_data.cookie); + data->host_cookie = 0; + } + + if (next || (!next && data->host_cookie != host->next_data.cookie)) + sg_count = rtsx_pci_dma_map_sg(pcr, + data->sg, data->sg_len, read); + else + sg_count = host->next_data.sg_count; + + if (next) { + next->sg_count = sg_count; + if (++next->cookie < 0) + next->cookie = 1; + data->host_cookie = next->cookie; + } + + return sg_count; +} + +static void sdmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, + bool is_first_req) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (data->host_cookie) { + dev_err(sdmmc_dev(host), + "error: descard already cookie data[%d]\n", + data->host_cookie); + data->host_cookie = 0; + } + + dev_dbg(sdmmc_dev(host), "dma sg prepared: %d\n", + sd_pre_dma_transfer(host, data, &host->next_data)); +} + +static void sdmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, + int err) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + struct mmc_data *data = mrq->data; + int read = data->flags & MMC_DATA_READ; + + rtsx_pci_dma_unmap_sg(pcr, data->sg, data->sg_len, read); + data->host_cookie = 0; +} + +static int sd_start_multi_rw(struct realtek_pci_sdmmc *host, + struct mmc_request *mrq) { struct rtsx_pcr *pcr = host->pcr; struct mmc_host *mmc = host->mmc; struct mmc_card *card = mmc->card; struct mmc_data *data = mrq->data; int uhs = mmc_card_uhs(card); - int read = (data->flags & MMC_DATA_READ) ? 1 : 0; + int read = data->flags & MMC_DATA_READ; u8 cfg2, trans_mode; int err; size_t data_len = data->blksz * data->blocks; + if (host->data) + dev_err(sdmmc_dev(host), "error: data already exist\n"); + + host->data = data; + if (read) { cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0; @@ -420,17 +635,56 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, SD_TRANSFER_END, SD_TRANSFER_END); + mod_timer(&host->timer, jiffies + 10 * HZ); rtsx_pci_send_cmd_no_wait(pcr); - err = rtsx_pci_transfer_data(pcr, data->sg, data->sg_len, read, 10000); + err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, read); if (err < 0) { - sd_clear_error(host); - return err; + data->error = err; + tasklet_schedule(&host->finish_tasklet); } - return 0; } +static void sd_finish_multi_rw(unsigned long host_addr) +{ + struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr; + struct rtsx_pcr *pcr = host->pcr; + struct mmc_data *data; + int err = 0; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + if (!host->data) { + dev_err(sdmmc_dev(host), "error: no data exist\n"); + goto out; + } + + data = host->data; + host->data = NULL; + + if (pcr->trans_result == TRANS_NO_DEVICE) + err = -ENODEV; + else if (pcr->trans_result != TRANS_RESULT_OK) + err = -EINVAL; + + if (err < 0) { + data->error = err; + goto out; + } + + if (!host->mrq->sbc && data->stop) { + sd_send_cmd(host, data->stop); + spin_unlock_irqrestore(&host->lock, flags); + return; + } + +out: + tasklet_schedule(&host->finish_tasklet); + spin_unlock_irqrestore(&host->lock, flags); +} + static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host) { rtsx_pci_write_register(host->pcr, SD_CFG1, @@ -511,85 +765,47 @@ static int sd_change_phase(struct realtek_pci_sdmmc *host, return 0; } -static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map) +static inline u32 test_phase_bit(u32 phase_map, unsigned int bit) { - struct timing_phase_path path[MAX_PHASE + 1]; - int i, j, cont_path_cnt; - int new_block, max_len, final_path_idx; - u8 final_phase = 0xFF; + bit %= RTSX_PHASE_MAX; + return phase_map & (1 << bit); +} - /* Parse phase_map, take it as a bit-ring */ - cont_path_cnt = 0; - new_block = 1; - j = 0; - for (i = 0; i < MAX_PHASE + 1; i++) { - if (phase_map & (1 << i)) { - if (new_block) { - new_block = 0; - j = cont_path_cnt++; - path[j].start = i; - path[j].end = i; - } else { - path[j].end = i; - } - } else { - new_block = 1; - if (cont_path_cnt) { - /* Calculate path length and middle point */ - int idx = cont_path_cnt - 1; - path[idx].len = - path[idx].end - path[idx].start + 1; - path[idx].mid = - path[idx].start + path[idx].len / 2; - } - } - } +static int sd_get_phase_len(u32 phase_map, unsigned int start_bit) +{ + int i; - if (cont_path_cnt == 0) { - dev_dbg(sdmmc_dev(host), "No continuous phase path\n"); - goto finish; - } else { - /* Calculate last continuous path length and middle point */ - int idx = cont_path_cnt - 1; - path[idx].len = path[idx].end - path[idx].start + 1; - path[idx].mid = path[idx].start + path[idx].len / 2; + for (i = 0; i < RTSX_PHASE_MAX; i++) { + if (test_phase_bit(phase_map, start_bit + i) == 0) + return i; } + return RTSX_PHASE_MAX; +} + +static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map) +{ + int start = 0, len = 0; + int start_final = 0, len_final = 0; + u8 final_phase = 0xFF; - /* Connect the first and last continuous paths if they are adjacent */ - if (!path[0].start && (path[cont_path_cnt - 1].end == MAX_PHASE)) { - /* Using negative index */ - path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1; - path[0].len += path[cont_path_cnt - 1].len; - path[0].mid = path[0].start + path[0].len / 2; - /* Convert negative middle point index to positive one */ - if (path[0].mid < 0) - path[0].mid += MAX_PHASE + 1; - cont_path_cnt--; + if (phase_map == 0) { + dev_err(sdmmc_dev(host), "phase error: [map:%x]\n", phase_map); + return final_phase; } - /* Choose the longest continuous phase path */ - max_len = 0; - final_phase = 0; - final_path_idx = 0; - for (i = 0; i < cont_path_cnt; i++) { - if (path[i].len > max_len) { - max_len = path[i].len; - final_phase = (u8)path[i].mid; - final_path_idx = i; + while (start < RTSX_PHASE_MAX) { + len = sd_get_phase_len(phase_map, start); + if (len_final < len) { + start_final = start; + len_final = len; } - - dev_dbg(sdmmc_dev(host), "path[%d].start = %d\n", - i, path[i].start); - dev_dbg(sdmmc_dev(host), "path[%d].end = %d\n", - i, path[i].end); - dev_dbg(sdmmc_dev(host), "path[%d].len = %d\n", - i, path[i].len); - dev_dbg(sdmmc_dev(host), "path[%d].mid = %d\n", - i, path[i].mid); + start += len ? len : 1; } -finish: - dev_dbg(sdmmc_dev(host), "Final chosen phase: %d\n", final_phase); + final_phase = (start_final + len_final / 2) % RTSX_PHASE_MAX; + dev_dbg(sdmmc_dev(host), "phase: [map:%x] [maxlen:%d] [final:%d]\n", + phase_map, len_final, final_phase); + return final_phase; } @@ -635,7 +851,7 @@ static int sd_tuning_phase(struct realtek_pci_sdmmc *host, int err, i; u32 raw_phase_map = 0; - for (i = MAX_PHASE; i >= 0; i--) { + for (i = 0; i < RTSX_PHASE_MAX; i++) { err = sd_tuning_rx_cmd(host, opcode, (u8)i); if (err == 0) raw_phase_map |= 1 << i; @@ -685,6 +901,13 @@ static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode) return 0; } +static inline bool sd_use_muti_rw(struct mmc_command *cmd) +{ + return mmc_op_multi(cmd->opcode) || + (cmd->opcode == MMC_READ_SINGLE_BLOCK) || + (cmd->opcode == MMC_WRITE_BLOCK); +} + static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct realtek_pci_sdmmc *host = mmc_priv(mmc); @@ -693,6 +916,14 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_data *data = mrq->data; unsigned int data_size = 0; int err; + unsigned long flags; + + mutex_lock(&pcr->pcr_mutex); + spin_lock_irqsave(&host->lock, flags); + + if (host->mrq) + dev_err(sdmmc_dev(host), "error: request already exist\n"); + host->mrq = mrq; if (host->eject) { cmd->error = -ENOMEDIUM; @@ -705,8 +936,6 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) goto finish; } - mutex_lock(&pcr->pcr_mutex); - rtsx_pci_start_run(pcr); rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth, @@ -715,46 +944,28 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) rtsx_pci_write_register(pcr, CARD_SHARE_MODE, CARD_SHARE_MASK, CARD_SHARE_48_SD); - mutex_lock(&host->host_mutex); - host->mrq = mrq; - mutex_unlock(&host->host_mutex); - if (mrq->data) data_size = data->blocks * data->blksz; - if (!data_size || mmc_op_multi(cmd->opcode) || - (cmd->opcode == MMC_READ_SINGLE_BLOCK) || - (cmd->opcode == MMC_WRITE_BLOCK)) { - sd_send_cmd_get_rsp(host, cmd); - - if (!cmd->error && data_size) { - sd_rw_multi(host, mrq); + if (sd_use_muti_rw(cmd)) + host->sg_count = sd_pre_dma_transfer(host, data, NULL); - if (mmc_op_multi(cmd->opcode) && mrq->stop) - sd_send_cmd_get_rsp(host, mrq->stop); - } + if (!data_size || sd_use_muti_rw(cmd)) { + if (mrq->sbc) + sd_send_cmd(host, mrq->sbc); + else + sd_send_cmd(host, cmd); + spin_unlock_irqrestore(&host->lock, flags); } else { + spin_unlock_irqrestore(&host->lock, flags); sd_normal_rw(host, mrq); + tasklet_schedule(&host->finish_tasklet); } - - if (mrq->data) { - if (cmd->error || data->error) - data->bytes_xfered = 0; - else - data->bytes_xfered = data->blocks * data->blksz; - } - - mutex_unlock(&pcr->pcr_mutex); + return; finish: - if (cmd->error) - dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error); - - mutex_lock(&host->host_mutex); - host->mrq = NULL; - mutex_unlock(&host->host_mutex); - - mmc_request_done(mmc, mrq); + tasklet_schedule(&host->finish_tasklet); + spin_unlock_irqrestore(&host->lock, flags); } static int sd_set_bus_width(struct realtek_pci_sdmmc *host, @@ -1189,6 +1400,8 @@ out: } static const struct mmc_host_ops realtek_pci_sdmmc_ops = { + .pre_req = sdmmc_pre_req, + .post_req = sdmmc_post_req, .request = sdmmc_request, .set_ios = sdmmc_set_ios, .get_ro = sdmmc_get_ro, @@ -1252,6 +1465,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) struct realtek_pci_sdmmc *host; struct rtsx_pcr *pcr; struct pcr_handle *handle = pdev->dev.platform_data; + unsigned long host_addr; if (!handle) return -ENXIO; @@ -1275,8 +1489,15 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) pcr->slots[RTSX_SD_CARD].p_dev = pdev; pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event; - mutex_init(&host->host_mutex); + host_addr = (unsigned long)host; + host->next_data.cookie = 1; + setup_timer(&host->timer, sd_request_timeout, host_addr); + tasklet_init(&host->cmd_tasklet, sd_get_rsp, host_addr); + tasklet_init(&host->data_tasklet, sd_finish_multi_rw, host_addr); + tasklet_init(&host->finish_tasklet, sd_finish_request, host_addr); + spin_lock_init(&host->lock); + pcr->slots[RTSX_SD_CARD].done_transfer = sd_isr_done_transfer; realtek_init_host(host); mmc_add_host(mmc); @@ -1289,6 +1510,8 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); struct rtsx_pcr *pcr; struct mmc_host *mmc; + struct mmc_request *mrq; + unsigned long flags; if (!host) return 0; @@ -1296,25 +1519,37 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) pcr = host->pcr; pcr->slots[RTSX_SD_CARD].p_dev = NULL; pcr->slots[RTSX_SD_CARD].card_event = NULL; + pcr->slots[RTSX_SD_CARD].done_transfer = NULL; mmc = host->mmc; - host->eject = true; + mrq = host->mrq; - mutex_lock(&host->host_mutex); + spin_lock_irqsave(&host->lock, flags); if (host->mrq) { dev_dbg(&(pdev->dev), "%s: Controller removed during transfer\n", mmc_hostname(mmc)); - rtsx_pci_complete_unfinished_transfer(pcr); + if (mrq->sbc) + mrq->sbc->error = -ENOMEDIUM; + if (mrq->cmd) + mrq->cmd->error = -ENOMEDIUM; + if (mrq->stop) + mrq->stop->error = -ENOMEDIUM; + if (mrq->data) + mrq->data->error = -ENOMEDIUM; - host->mrq->cmd->error = -ENOMEDIUM; - if (host->mrq->stop) - host->mrq->stop->error = -ENOMEDIUM; - mmc_request_done(mmc, host->mrq); + tasklet_schedule(&host->finish_tasklet); } - mutex_unlock(&host->host_mutex); + spin_unlock_irqrestore(&host->lock, flags); + + del_timer_sync(&host->timer); + tasklet_kill(&host->cmd_tasklet); + tasklet_kill(&host->data_tasklet); + tasklet_kill(&host->finish_tasklet); mmc_remove_host(mmc); + host->eject = true; + mmc_free_host(mmc); dev_dbg(&(pdev->dev), |