diff options
author | Peter Robinson <pbrobinson@gmail.com> | 2013-09-04 02:43:18 +0100 |
---|---|---|
committer | Peter Robinson <pbrobinson@gmail.com> | 2013-09-04 02:43:18 +0100 |
commit | bad69a54fc999f7ed3871b472a31c1a760ed1e81 (patch) | |
tree | 16a0df217b495c859746080ca7f3cc4e1ff26366 /0012-mmc-omap_hsmmc-Fix-the-crashes-due-to-the-interrupts.patch | |
parent | 7ad7e4c4014b96bb176b000b8be71063cf5fc60f (diff) | |
download | kernel-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.patch | 303 |
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 + |