summaryrefslogtreecommitdiffstats
path: root/0012-mmc-omap_hsmmc-Fix-the-crashes-due-to-the-interrupts.patch
diff options
context:
space:
mode:
authorPeter Robinson <pbrobinson@gmail.com>2013-09-04 02:43:18 +0100
committerPeter Robinson <pbrobinson@gmail.com>2013-09-04 02:43:18 +0100
commitbad69a54fc999f7ed3871b472a31c1a760ed1e81 (patch)
tree16a0df217b495c859746080ca7f3cc4e1ff26366 /0012-mmc-omap_hsmmc-Fix-the-crashes-due-to-the-interrupts.patch
parent7ad7e4c4014b96bb176b000b8be71063cf5fc60f (diff)
downloadkernel-bad69a54fc999f7ed3871b472a31c1a760ed1e81.tar.gz
kernel-bad69a54fc999f7ed3871b472a31c1a760ed1e81.tar.xz
kernel-bad69a54fc999f7ed3871b472a31c1a760ed1e81.zip
Add patch set to fix MMC on AM33xx, Add basic support for BeagleBone Black
Diffstat (limited to '0012-mmc-omap_hsmmc-Fix-the-crashes-due-to-the-interrupts.patch')
-rw-r--r--0012-mmc-omap_hsmmc-Fix-the-crashes-due-to-the-interrupts.patch303
1 files changed, 303 insertions, 0 deletions
diff --git a/0012-mmc-omap_hsmmc-Fix-the-crashes-due-to-the-interrupts.patch b/0012-mmc-omap_hsmmc-Fix-the-crashes-due-to-the-interrupts.patch
new file mode 100644
index 000000000..1c83398d0
--- /dev/null
+++ b/0012-mmc-omap_hsmmc-Fix-the-crashes-due-to-the-interrupts.patch
@@ -0,0 +1,303 @@
+From b13c0c62ddf7a3a7d5b96fed8ea80f21f3bb2dad Mon Sep 17 00:00:00 2001
+From: Pantelis Antoniou <panto@antoniou-consulting.com>
+Date: Wed, 17 Jul 2013 20:00:13 +0300
+Subject: [PATCH 12/13] mmc: omap_hsmmc: Fix the crashes due to the interrupts
+ racing
+
+---
+ drivers/mmc/host/omap_hsmmc.c | 120 +++++++++++++++++++++++++++++++-----------
+ 1 file changed, 88 insertions(+), 32 deletions(-)
+
+diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
+index 1f9ff97..91e2954 100644
+--- a/drivers/mmc/host/omap_hsmmc.c
++++ b/drivers/mmc/host/omap_hsmmc.c
+@@ -171,7 +171,7 @@ struct omap_hsmmc_host {
+ unsigned char power_mode;
+ int suspended;
+ int irq;
+- int use_dma, dma_ch;
++ int use_dma;
+ struct dma_chan *tx_chan;
+ struct dma_chan *rx_chan;
+ int slot_id;
+@@ -180,10 +180,15 @@ struct omap_hsmmc_host {
+ int protect_card;
+ int reqs_blocked;
+ int use_reg;
+- int req_in_progress;
+ struct omap_hsmmc_next next_data;
+
+ struct omap_mmc_platform_data *pdata;
++
++ unsigned int req_flags;
++#define RQF_REQ_IN_PROGRESS (1 << 0)
++#define RQF_DMA_IN_PROGRESS (1 << 1)
++#define RQF_REQ_DONE (1 << 2)
++#define RQF_DMA_DONE (1 << 3)
+ };
+
+ static int omap_hsmmc_card_detect(struct device *dev, int slot)
+@@ -803,7 +808,8 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
+ if (host->use_dma)
+ cmdreg |= DMAE;
+
+- host->req_in_progress = 1;
++ host->req_flags |= RQF_REQ_IN_PROGRESS;
++ host->req_flags &= ~RQF_REQ_DONE;
+
+ OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
+ OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
+@@ -826,19 +832,34 @@ static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host,
+
+ static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
+ {
+- int dma_ch;
++ int completed;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->irq_lock, flags);
+- host->req_in_progress = 0;
+- dma_ch = host->dma_ch;
+- spin_unlock_irqrestore(&host->irq_lock, flags);
++
++ host->req_flags &= ~RQF_REQ_IN_PROGRESS;
++ host->req_flags |= RQF_REQ_DONE;
++
++ /* completed? */
++ if (mrq->data && host->use_dma)
++ completed = (host->req_flags & RQF_DMA_DONE) == RQF_DMA_DONE;
++ else
++ completed = 1;
+
+ omap_hsmmc_disable_irq(host);
++
+ /* Do not complete the request if DMA is still in progress */
+- if (mrq->data && host->use_dma && dma_ch != -1)
++ if (!completed) {
++ spin_unlock_irqrestore(&host->irq_lock, flags);
++ pr_debug("%s: not completed!\n", __func__);
+ return;
++ }
++
++ /* clear the flags now */
++ host->req_flags &= ~(RQF_REQ_DONE | RQF_DMA_DONE);
+ host->mrq = NULL;
++ spin_unlock_irqrestore(&host->irq_lock, flags);
++
+ mmc_request_done(host->mmc, mrq);
+ }
+
+@@ -855,6 +876,7 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
+ if (host->cmd && host->cmd->opcode == 6 &&
+ host->response_busy) {
+ host->response_busy = 0;
++ pr_debug("%s: response_busy = 0\n", __func__);
+ return;
+ }
+
+@@ -870,9 +892,11 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
+ data->bytes_xfered = 0;
+
+ if (!data->stop) {
++ pr_debug("%s: calling omap_hsmmc_request_done\n", __func__);
+ omap_hsmmc_request_done(host, data->mrq);
+ return;
+ }
++ pr_debug("%s: calling omap_hsmmc_start_command\n", __func__);
+ omap_hsmmc_start_command(host, data->stop, NULL);
+ }
+
+@@ -882,6 +906,8 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
+ static void
+ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
+ {
++ unsigned long flags;
++
+ host->cmd = NULL;
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+@@ -898,6 +924,18 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
+ }
+ if ((host->data == NULL && !host->response_busy) || cmd->error)
+ omap_hsmmc_request_done(host, cmd->mrq);
++ else {
++ spin_lock_irqsave(&host->irq_lock, flags);
++ /* we use DMA, and DMA is completed - kick the can */
++ if ((host->req_flags & RQF_DMA_DONE) != 0) {
++ host->req_flags &= ~(RQF_REQ_IN_PROGRESS | RQF_REQ_DONE | RQF_DMA_DONE);
++ host->mrq = NULL;
++ mmc_request_done(host->mmc, cmd->mrq);
++ } else {
++ pr_debug("%s: not calling omap_hsmmc_request_done!\n", __func__);
++ }
++ spin_unlock_irqrestore(&host->irq_lock, flags);
++ }
+ }
+
+ /*
+@@ -905,17 +943,19 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
+ */
+ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
+ {
+- int dma_ch;
++ int dma_in_progress;
+ unsigned long flags;
+
+ host->data->error = errno;
+
+ spin_lock_irqsave(&host->irq_lock, flags);
+- dma_ch = host->dma_ch;
+- host->dma_ch = -1;
++ dma_in_progress = host->use_dma &&
++ (host->req_flags & RQF_DMA_IN_PROGRESS) != 0;
++ host->req_flags &= ~RQF_DMA_IN_PROGRESS;
++ host->req_flags |= RQF_DMA_DONE;
+ spin_unlock_irqrestore(&host->irq_lock, flags);
+
+- if (host->use_dma && dma_ch != -1) {
++ if (dma_in_progress) {
+ struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data);
+
+ dmaengine_terminate_all(chan);
+@@ -1005,16 +1045,22 @@ static void hsmmc_command_incomplete(struct omap_hsmmc_host *host,
+ int err, int end_cmd)
+ {
+ if (end_cmd) {
++ pr_debug("%s end_cmd\n", __func__);
+ omap_hsmmc_reset_controller_fsm(host, SRC);
+ if (host->cmd)
+ host->cmd->error = err;
+ }
+
+ if (host->data) {
++ pr_debug("%s host->data; resetting dma\n", __func__);
+ omap_hsmmc_reset_controller_fsm(host, SRD);
+ omap_hsmmc_dma_cleanup(host, err);
+- } else if (host->mrq && host->mrq->cmd)
++ } else if (host->mrq && host->mrq->cmd) {
++ pr_debug("%s error\n", __func__);
+ host->mrq->cmd->error = err;
++ } else {
++ pr_debug("%s nothing\n", __func__);
++ }
+ }
+
+ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
+@@ -1055,13 +1101,13 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
+ struct omap_hsmmc_host *host = dev_id;
+ int status;
+
+- status = OMAP_HSMMC_READ(host->base, STAT);
+- while (status & INT_EN_MASK && host->req_in_progress) {
+- omap_hsmmc_do_irq(host, status);
++ while ((status = OMAP_HSMMC_READ(host->base, STAT)) & INT_EN_MASK) {
++
++ if (host->req_flags & RQF_REQ_IN_PROGRESS)
++ omap_hsmmc_do_irq(host, status);
+
+ /* Flush posted write */
+ OMAP_HSMMC_WRITE(host->base, STAT, status);
+- status = OMAP_HSMMC_READ(host->base, STAT);
+ }
+
+ return IRQ_HANDLED;
+@@ -1199,13 +1245,15 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
+ static void omap_hsmmc_dma_callback(void *param)
+ {
+ struct omap_hsmmc_host *host = param;
++ struct mmc_request *mrq = host->mrq;
+ struct dma_chan *chan;
+ struct mmc_data *data;
+- int req_in_progress;
++ int completed;
+
+ spin_lock_irq(&host->irq_lock);
+- if (host->dma_ch < 0) {
++ if ((host->req_flags & RQF_DMA_IN_PROGRESS) == 0) {
+ spin_unlock_irq(&host->irq_lock);
++ pr_debug("%s: No DMA in progress!\n", __func__);
+ return;
+ }
+
+@@ -1216,17 +1264,22 @@ static void omap_hsmmc_dma_callback(void *param)
+ data->sg, data->sg_len,
+ omap_hsmmc_get_dma_dir(host, data));
+
+- req_in_progress = host->req_in_progress;
+- host->dma_ch = -1;
+- spin_unlock_irq(&host->irq_lock);
++ host->req_flags &= ~RQF_DMA_IN_PROGRESS;
++ host->req_flags |= RQF_DMA_DONE;
+
+- /* If DMA has finished after TC, complete the request */
+- if (!req_in_progress) {
+- struct mmc_request *mrq = host->mrq;
++ completed = (host->req_flags & RQF_REQ_DONE) != 0;
+
+- host->mrq = NULL;
+- mmc_request_done(host->mmc, mrq);
++ if (!completed) {
++ spin_unlock_irq(&host->irq_lock);
++ pr_debug("%s: not completed\n", __func__);
++ return;
+ }
++
++ host->req_flags &= ~(RQF_REQ_DONE | RQF_DMA_DONE);
++ host->mrq = NULL;
++ spin_unlock_irq(&host->irq_lock);
++
++ mmc_request_done(host->mmc, mrq);
+ }
+
+ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
+@@ -1294,7 +1347,7 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
+ */
+ return -EINVAL;
+
+- BUG_ON(host->dma_ch != -1);
++ BUG_ON((host->req_flags & RQF_DMA_IN_PROGRESS) != 0);
+
+ chan = omap_hsmmc_get_dma_chan(host, data);
+
+@@ -1328,7 +1381,7 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
+ /* Does not fail */
+ dmaengine_submit(tx);
+
+- host->dma_ch = 1;
++ host->req_flags |= RQF_DMA_IN_PROGRESS;
+
+ dma_async_issue_pending(chan);
+
+@@ -1448,8 +1501,11 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
+ int err;
+
+- BUG_ON(host->req_in_progress);
+- BUG_ON(host->dma_ch != -1);
++ BUG_ON((host->req_flags & RQF_REQ_IN_PROGRESS) != 0);
++ BUG_ON((host->req_flags & RQF_REQ_DONE) != 0);
++ BUG_ON((host->req_flags & RQF_DMA_IN_PROGRESS) != 0);
++ BUG_ON((host->req_flags & RQF_DMA_DONE) != 0);
++
+ if (host->protect_card) {
+ if (host->reqs_blocked < 3) {
+ /*
+@@ -1826,13 +1882,13 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
+ host->pdata = pdata;
+ host->dev = &pdev->dev;
+ host->use_dma = 1;
+- host->dma_ch = -1;
+ host->irq = irq;
+ host->slot_id = 0;
+ host->mapbase = res->start + pdata->reg_offset;
+ host->base = ioremap(host->mapbase, SZ_4K);
+ host->power_mode = MMC_POWER_OFF;
+ host->next_data.cookie = 1;
++ host->req_flags = 0;
+
+ platform_set_drvdata(pdev, host);
+
+--
+1.8.2.1
+