diff options
Diffstat (limited to 'drivers/mmc/host/mmci.c')
-rw-r--r-- | drivers/mmc/host/mmci.c | 39 |
1 files changed, 32 insertions, 7 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 56e9a416826..0726e59fd41 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -29,6 +29,7 @@ #include <linux/dmaengine.h> #include <linux/dma-mapping.h> #include <linux/amba/mmci.h> +#include <linux/pm_runtime.h> #include <asm/div64.h> #include <asm/io.h> @@ -170,6 +171,7 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) * back into the driver... */ spin_unlock(&host->lock); + pm_runtime_put(mmc_dev(host->mmc)); mmc_request_done(host->mmc, mrq); spin_lock(&host->lock); } @@ -464,7 +466,7 @@ static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) struct mmci_host_next *next = &host->next_data; if (data->host_cookie && data->host_cookie != next->cookie) { - printk(KERN_WARNING "[%s] invalid cookie: data->host_cookie %d" + pr_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; @@ -529,7 +531,7 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, if (chan) { if (err) dmaengine_terminate_all(chan); - if (err || data->host_cookie) + if (data->host_cookie) dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, dir); mrq->data->host_cookie = 0; @@ -673,7 +675,8 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, unsigned int status) { /* First check for errors */ - if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { + if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR| + MCI_TXUNDERRUN|MCI_RXOVERRUN)) { u32 remain, success; /* Terminate the DMA transfer */ @@ -752,8 +755,12 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, } if (!cmd->data || cmd->error) { - if (host->data) + if (host->data) { + /* Terminate the DMA transfer */ + if (dma_inprogress(host)) + mmci_dma_data_error(host); mmci_stop_data(host); + } mmci_request_end(host, cmd->mrq); } else if (!(cmd->data->flags & MMC_DATA_READ)) { mmci_start_data(host, cmd->data); @@ -953,8 +960,9 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status); data = host->data; - if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN| - MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data) + if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR| + MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_DATAEND| + MCI_DATABLOCKEND) && data) mmci_data_irq(host, data, status); cmd = host->cmd; @@ -984,6 +992,8 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) return; } + pm_runtime_get_sync(mmc_dev(mmc)); + spin_lock_irqsave(&host->lock, flags); host->mrq = mrq; @@ -1156,10 +1166,14 @@ static int __devinit mmci_probe(struct amba_device *dev, goto host_free; } - ret = clk_enable(host->clk); + ret = clk_prepare(host->clk); if (ret) goto clk_free; + ret = clk_enable(host->clk); + if (ret) + goto clk_unprep; + host->plat = plat; host->variant = variant; host->mclk = clk_get_rate(host->clk); @@ -1327,6 +1341,8 @@ static int __devinit mmci_probe(struct amba_device *dev, mmci_dma_setup(host); + pm_runtime_put(&dev->dev); + mmc_add_host(mmc); return 0; @@ -1345,6 +1361,8 @@ static int __devinit mmci_probe(struct amba_device *dev, iounmap(host->base); clk_disable: clk_disable(host->clk); + clk_unprep: + clk_unprepare(host->clk); clk_free: clk_put(host->clk); host_free: @@ -1364,6 +1382,12 @@ static int __devexit mmci_remove(struct amba_device *dev) if (mmc) { struct mmci_host *host = mmc_priv(mmc); + /* + * Undo pm_runtime_put() in probe. We use the _sync + * version here so that we can access the primecell. + */ + pm_runtime_get_sync(&dev->dev); + mmc_remove_host(mmc); writel(0, host->base + MMCIMASK0); @@ -1386,6 +1410,7 @@ static int __devexit mmci_remove(struct amba_device *dev) iounmap(host->base); clk_disable(host->clk); + clk_unprepare(host->clk); clk_put(host->clk); if (host->vcc) |