summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/rtsx_pci_sdmmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/rtsx_pci_sdmmc.c')
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c529
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),