|
44 | 44 | #include <mach/hardware.h>
|
45 | 45 |
|
46 | 46 | #define DRIVER_NAME "mxc-mmc"
|
| 47 | +#define MXCMCI_TIMEOUT_MS 10000 |
47 | 48 |
|
48 | 49 | #define MMC_REG_STR_STP_CLK 0x00
|
49 | 50 | #define MMC_REG_STATUS 0x04
|
@@ -150,6 +151,8 @@ struct mxcmci_host {
|
150 | 151 | int dmareq;
|
151 | 152 | struct dma_slave_config dma_slave_config;
|
152 | 153 | struct imx_dma_data dma_data;
|
| 154 | + |
| 155 | + struct timer_list watchdog; |
153 | 156 | };
|
154 | 157 |
|
155 | 158 | static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
|
@@ -271,9 +274,32 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
|
271 | 274 | dmaengine_submit(host->desc);
|
272 | 275 | dma_async_issue_pending(host->dma);
|
273 | 276 |
|
| 277 | + mod_timer(&host->watchdog, jiffies + msecs_to_jiffies(MXCMCI_TIMEOUT_MS)); |
| 278 | + |
274 | 279 | return 0;
|
275 | 280 | }
|
276 | 281 |
|
| 282 | +static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat); |
| 283 | +static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat); |
| 284 | + |
| 285 | +static void mxcmci_dma_callback(void *data) |
| 286 | +{ |
| 287 | + struct mxcmci_host *host = data; |
| 288 | + u32 stat; |
| 289 | + |
| 290 | + del_timer(&host->watchdog); |
| 291 | + |
| 292 | + stat = readl(host->base + MMC_REG_STATUS); |
| 293 | + writel(stat & ~STATUS_DATA_TRANS_DONE, host->base + MMC_REG_STATUS); |
| 294 | + |
| 295 | + dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat); |
| 296 | + |
| 297 | + if (stat & STATUS_READ_OP_DONE) |
| 298 | + writel(STATUS_READ_OP_DONE, host->base + MMC_REG_STATUS); |
| 299 | + |
| 300 | + mxcmci_data_done(host, stat); |
| 301 | +} |
| 302 | + |
277 | 303 | static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
|
278 | 304 | unsigned int cmdat)
|
279 | 305 | {
|
@@ -305,8 +331,14 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
|
305 | 331 |
|
306 | 332 | int_cntr = INT_END_CMD_RES_EN;
|
307 | 333 |
|
308 |
| - if (mxcmci_use_dma(host)) |
309 |
| - int_cntr |= INT_READ_OP_EN | INT_WRITE_OP_DONE_EN; |
| 334 | + if (mxcmci_use_dma(host)) { |
| 335 | + if (host->dma_dir == DMA_FROM_DEVICE) { |
| 336 | + host->desc->callback = mxcmci_dma_callback; |
| 337 | + host->desc->callback_param = host; |
| 338 | + } else { |
| 339 | + int_cntr |= INT_WRITE_OP_DONE_EN; |
| 340 | + } |
| 341 | + } |
310 | 342 |
|
311 | 343 | spin_lock_irqsave(&host->lock, flags);
|
312 | 344 | if (host->use_sdio)
|
@@ -345,11 +377,9 @@ static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat)
|
345 | 377 | struct mmc_data *data = host->data;
|
346 | 378 | int data_error;
|
347 | 379 |
|
348 |
| - if (mxcmci_use_dma(host)) { |
349 |
| - dmaengine_terminate_all(host->dma); |
| 380 | + if (mxcmci_use_dma(host)) |
350 | 381 | dma_unmap_sg(host->dma->device->dev, data->sg, data->sg_len,
|
351 | 382 | host->dma_dir);
|
352 |
| - } |
353 | 383 |
|
354 | 384 | if (stat & STATUS_ERR_MASK) {
|
355 | 385 | dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",
|
@@ -624,8 +654,10 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
|
624 | 654 | mxcmci_cmd_done(host, stat);
|
625 | 655 |
|
626 | 656 | if (mxcmci_use_dma(host) &&
|
627 |
| - (stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE))) |
| 657 | + (stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE))) { |
| 658 | + del_timer(&host->watchdog); |
628 | 659 | mxcmci_data_done(host, stat);
|
| 660 | + } |
629 | 661 |
|
630 | 662 | if (host->default_irq_mask &&
|
631 | 663 | (stat & (STATUS_CARD_INSERTION | STATUS_CARD_REMOVAL)))
|
@@ -836,6 +868,34 @@ static bool filter(struct dma_chan *chan, void *param)
|
836 | 868 | return true;
|
837 | 869 | }
|
838 | 870 |
|
| 871 | +static void mxcmci_watchdog(unsigned long data) |
| 872 | +{ |
| 873 | + struct mmc_host *mmc = (struct mmc_host *)data; |
| 874 | + struct mxcmci_host *host = mmc_priv(mmc); |
| 875 | + struct mmc_request *req = host->req; |
| 876 | + unsigned int stat = readl(host->base + MMC_REG_STATUS); |
| 877 | + |
| 878 | + if (host->dma_dir == DMA_FROM_DEVICE) { |
| 879 | + dmaengine_terminate_all(host->dma); |
| 880 | + dev_err(mmc_dev(host->mmc), |
| 881 | + "%s: read time out (status = 0x%08x)\n", |
| 882 | + __func__, stat); |
| 883 | + } else { |
| 884 | + dev_err(mmc_dev(host->mmc), |
| 885 | + "%s: write time out (status = 0x%08x)\n", |
| 886 | + __func__, stat); |
| 887 | + mxcmci_softreset(host); |
| 888 | + } |
| 889 | + |
| 890 | + /* Mark transfer as erroneus and inform the upper layers */ |
| 891 | + |
| 892 | + host->data->error = -ETIMEDOUT; |
| 893 | + host->req = NULL; |
| 894 | + host->cmd = NULL; |
| 895 | + host->data = NULL; |
| 896 | + mmc_request_done(host->mmc, req); |
| 897 | +} |
| 898 | + |
839 | 899 | static const struct mmc_host_ops mxcmci_ops = {
|
840 | 900 | .request = mxcmci_request,
|
841 | 901 | .set_ios = mxcmci_set_ios,
|
@@ -968,6 +1028,10 @@ static int mxcmci_probe(struct platform_device *pdev)
|
968 | 1028 |
|
969 | 1029 | mmc_add_host(mmc);
|
970 | 1030 |
|
| 1031 | + init_timer(&host->watchdog); |
| 1032 | + host->watchdog.function = &mxcmci_watchdog; |
| 1033 | + host->watchdog.data = (unsigned long)mmc; |
| 1034 | + |
971 | 1035 | return 0;
|
972 | 1036 |
|
973 | 1037 | out_free_irq:
|
|
0 commit comments