|
@@ -141,6 +141,11 @@
|
|
|
#define OMAP_HSMMC_WRITE(base, reg, val) \
|
|
|
__raw_writel((val), (base) + OMAP_HSMMC_##reg)
|
|
|
|
|
|
+struct omap_hsmmc_next {
|
|
|
+ unsigned int dma_len;
|
|
|
+ s32 cookie;
|
|
|
+};
|
|
|
+
|
|
|
struct omap_hsmmc_host {
|
|
|
struct device *dev;
|
|
|
struct mmc_host *mmc;
|
|
@@ -184,6 +189,7 @@ struct omap_hsmmc_host {
|
|
|
int reqs_blocked;
|
|
|
int use_reg;
|
|
|
int req_in_progress;
|
|
|
+ struct omap_hsmmc_next next_data;
|
|
|
|
|
|
struct omap_mmc_platform_data *pdata;
|
|
|
};
|
|
@@ -1346,8 +1352,9 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
|
|
- omap_hsmmc_get_dma_dir(host, data));
|
|
|
+ if (!data->host_cookie)
|
|
|
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
|
|
+ omap_hsmmc_get_dma_dir(host, data));
|
|
|
|
|
|
req_in_progress = host->req_in_progress;
|
|
|
dma_ch = host->dma_ch;
|
|
@@ -1365,6 +1372,45 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
|
|
|
+ struct mmc_data *data,
|
|
|
+ struct omap_hsmmc_next *next)
|
|
|
+{
|
|
|
+ int dma_len;
|
|
|
+
|
|
|
+ if (!next && data->host_cookie &&
|
|
|
+ data->host_cookie != host->next_data.cookie) {
|
|
|
+ printk(KERN_WARNING "[%s] invalid cookie: data->host_cookie %d"
|
|
|
+ " host->next_data.cookie %d\n",
|
|
|
+ __func__, data->host_cookie, host->next_data.cookie);
|
|
|
+ data->host_cookie = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check if next job is already prepared */
|
|
|
+ if (next ||
|
|
|
+ (!next && data->host_cookie != host->next_data.cookie)) {
|
|
|
+ dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
|
|
+ data->sg_len,
|
|
|
+ omap_hsmmc_get_dma_dir(host, data));
|
|
|
+
|
|
|
+ } else {
|
|
|
+ dma_len = host->next_data.dma_len;
|
|
|
+ host->next_data.dma_len = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (dma_len == 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (next) {
|
|
|
+ next->dma_len = dma_len;
|
|
|
+ data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
|
|
|
+ } else
|
|
|
+ host->dma_len = dma_len;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Routine to configure and start DMA for the MMC card
|
|
|
*/
|
|
@@ -1398,9 +1444,10 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
|
|
|
mmc_hostname(host->mmc), ret);
|
|
|
return ret;
|
|
|
}
|
|
|
+ ret = omap_hsmmc_pre_dma_transfer(host, data, NULL);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
- host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
|
|
- data->sg_len, omap_hsmmc_get_dma_dir(host, data));
|
|
|
host->dma_ch = dma_ch;
|
|
|
host->dma_sg_idx = 0;
|
|
|
|
|
@@ -1480,6 +1527,35 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
|
|
+ int err)
|
|
|
+{
|
|
|
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
|
|
|
+ struct mmc_data *data = mrq->data;
|
|
|
+
|
|
|
+ if (host->use_dma) {
|
|
|
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
|
|
+ omap_hsmmc_get_dma_dir(host, data));
|
|
|
+ data->host_cookie = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
|
|
+ bool is_first_req)
|
|
|
+{
|
|
|
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
|
|
|
+
|
|
|
+ if (mrq->data->host_cookie) {
|
|
|
+ mrq->data->host_cookie = 0;
|
|
|
+ return ;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (host->use_dma)
|
|
|
+ if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
|
|
|
+ &host->next_data))
|
|
|
+ mrq->data->host_cookie = 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Request function. for read/write operation
|
|
|
*/
|
|
@@ -1928,6 +2004,8 @@ static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy)
|
|
|
static const struct mmc_host_ops omap_hsmmc_ops = {
|
|
|
.enable = omap_hsmmc_enable_fclk,
|
|
|
.disable = omap_hsmmc_disable_fclk,
|
|
|
+ .post_req = omap_hsmmc_post_req,
|
|
|
+ .pre_req = omap_hsmmc_pre_req,
|
|
|
.request = omap_hsmmc_request,
|
|
|
.set_ios = omap_hsmmc_set_ios,
|
|
|
.get_cd = omap_hsmmc_get_cd,
|
|
@@ -2077,6 +2155,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
|
|
|
host->mapbase = res->start;
|
|
|
host->base = ioremap(host->mapbase, SZ_4K);
|
|
|
host->power_mode = MMC_POWER_OFF;
|
|
|
+ host->next_data.cookie = 1;
|
|
|
|
|
|
platform_set_drvdata(pdev, host);
|
|
|
INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect);
|