304 lines
9.0 KiB
Diff
304 lines
9.0 KiB
Diff
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
|
|
|