|
@@ -140,6 +140,8 @@ struct mxcmci_host {
|
|
|
struct work_struct datawork;
|
|
|
};
|
|
|
|
|
|
+static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
|
|
|
+
|
|
|
static inline int mxcmci_use_dma(struct mxcmci_host *host)
|
|
|
{
|
|
|
return host->do_dma;
|
|
@@ -160,7 +162,7 @@ static void mxcmci_softreset(struct mxcmci_host *host)
|
|
|
writew(0xff, host->base + MMC_REG_RES_TO);
|
|
|
}
|
|
|
|
|
|
-static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
|
|
+static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
|
|
{
|
|
|
unsigned int nob = data->blocks;
|
|
|
unsigned int blksz = data->blksz;
|
|
@@ -168,6 +170,7 @@ static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
|
|
#ifdef HAS_DMA
|
|
|
struct scatterlist *sg;
|
|
|
int i;
|
|
|
+ int ret;
|
|
|
#endif
|
|
|
if (data->flags & MMC_DATA_STREAM)
|
|
|
nob = 0xffff;
|
|
@@ -183,7 +186,7 @@ static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
|
|
for_each_sg(data->sg, sg, data->sg_len, i) {
|
|
|
if (sg->offset & 3 || sg->length & 3) {
|
|
|
host->do_dma = 0;
|
|
|
- return;
|
|
|
+ return 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -192,23 +195,30 @@ static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
|
|
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
|
|
data->sg_len, host->dma_dir);
|
|
|
|
|
|
- imx_dma_setup_sg(host->dma, data->sg, host->dma_nents, datasize,
|
|
|
- host->res->start + MMC_REG_BUFFER_ACCESS,
|
|
|
- DMA_MODE_READ);
|
|
|
+ ret = imx_dma_setup_sg(host->dma, data->sg, host->dma_nents,
|
|
|
+ datasize,
|
|
|
+ host->res->start + MMC_REG_BUFFER_ACCESS,
|
|
|
+ DMA_MODE_READ);
|
|
|
} else {
|
|
|
host->dma_dir = DMA_TO_DEVICE;
|
|
|
host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
|
|
data->sg_len, host->dma_dir);
|
|
|
|
|
|
- imx_dma_setup_sg(host->dma, data->sg, host->dma_nents, datasize,
|
|
|
- host->res->start + MMC_REG_BUFFER_ACCESS,
|
|
|
- DMA_MODE_WRITE);
|
|
|
+ ret = imx_dma_setup_sg(host->dma, data->sg, host->dma_nents,
|
|
|
+ datasize,
|
|
|
+ host->res->start + MMC_REG_BUFFER_ACCESS,
|
|
|
+ DMA_MODE_WRITE);
|
|
|
}
|
|
|
|
|
|
+ if (ret) {
|
|
|
+ dev_err(mmc_dev(host->mmc), "failed to setup DMA : %d\n", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
wmb();
|
|
|
|
|
|
imx_dma_enable(host->dma);
|
|
|
#endif /* HAS_DMA */
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
|
|
@@ -345,8 +355,11 @@ static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
|
|
|
stat = readl(host->base + MMC_REG_STATUS);
|
|
|
if (stat & STATUS_ERR_MASK)
|
|
|
return stat;
|
|
|
- if (time_after(jiffies, timeout))
|
|
|
+ if (time_after(jiffies, timeout)) {
|
|
|
+ mxcmci_softreset(host);
|
|
|
+ mxcmci_set_clk_rate(host, host->clock);
|
|
|
return STATUS_TIME_OUT_READ;
|
|
|
+ }
|
|
|
if (stat & mask)
|
|
|
return 0;
|
|
|
cpu_relax();
|
|
@@ -531,6 +544,7 @@ static void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req)
|
|
|
{
|
|
|
struct mxcmci_host *host = mmc_priv(mmc);
|
|
|
unsigned int cmdat = host->cmdat;
|
|
|
+ int error;
|
|
|
|
|
|
WARN_ON(host->req != NULL);
|
|
|
|
|
@@ -540,7 +554,12 @@ static void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req)
|
|
|
host->do_dma = 1;
|
|
|
#endif
|
|
|
if (req->data) {
|
|
|
- mxcmci_setup_data(host, req->data);
|
|
|
+ error = mxcmci_setup_data(host, req->data);
|
|
|
+ if (error) {
|
|
|
+ req->cmd->error = error;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
cmdat |= CMD_DAT_CONT_DATA_ENABLE;
|
|
|
|
|
@@ -548,7 +567,9 @@ static void mxcmci_request(struct mmc_host *mmc, struct mmc_request *req)
|
|
|
cmdat |= CMD_DAT_CONT_WRITE;
|
|
|
}
|
|
|
|
|
|
- if (mxcmci_start_cmd(host, req->cmd, cmdat))
|
|
|
+ error = mxcmci_start_cmd(host, req->cmd, cmdat);
|
|
|
+out:
|
|
|
+ if (error)
|
|
|
mxcmci_finish_request(host, req);
|
|
|
}
|
|
|
|
|
@@ -724,7 +745,9 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|
|
goto out_clk_put;
|
|
|
}
|
|
|
|
|
|
- mmc->f_min = clk_get_rate(host->clk) >> 7;
|
|
|
+ mmc->f_min = clk_get_rate(host->clk) >> 16;
|
|
|
+ if (mmc->f_min < 400000)
|
|
|
+ mmc->f_min = 400000;
|
|
|
mmc->f_max = clk_get_rate(host->clk) >> 1;
|
|
|
|
|
|
/* recommended in data sheet */
|