1
0
mirror of https://git.yoctoproject.org/meta-ti synced 2026-07-16 22:38:04 +00:00
Files
meta-ti/recipes-kernel/linux/linux-omap-2.6.39/sakoman/0011-Revert-omap_hsmmc-improve-interrupt-synchronisation.patch
2011-06-05 11:52:20 +02:00

483 lines
14 KiB
Diff

From 3a66cefdf60033381c623b0425345c41e8c078fe Mon Sep 17 00:00:00 2001
From: Steve Sakoman <steve@sakoman.com>
Date: Fri, 19 Nov 2010 15:11:19 -0800
Subject: [PATCH 11/32] Revert "omap_hsmmc: improve interrupt synchronisation"
This reverts commit b417577d3b9bbb06a4ddc9aa955af9bd503f7242.
Conflicts:
drivers/mmc/host/omap_hsmmc.c
---
drivers/mmc/host/omap_hsmmc.c | 267 ++++++++++++++++++++---------------------
1 files changed, 128 insertions(+), 139 deletions(-)
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 259ece0..15a023b 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -159,10 +159,12 @@ struct omap_hsmmc_host {
*/
struct regulator *vcc;
struct regulator *vcc_aux;
+ struct semaphore sem;
struct work_struct mmc_carddetect_work;
void __iomem *base;
resource_size_t mapbase;
spinlock_t irq_lock; /* Prevent races with irq handler */
+ unsigned long flags;
unsigned int id;
unsigned int dma_len;
unsigned int dma_sg_idx;
@@ -183,7 +185,6 @@ struct omap_hsmmc_host {
int protect_card;
int reqs_blocked;
int use_reg;
- int req_in_progress;
struct omap_mmc_platform_data *pdata;
};
@@ -556,32 +557,6 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
}
-static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
- struct mmc_command *cmd)
-{
- unsigned int irq_mask;
-
- if (host->use_dma)
- irq_mask = INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE);
- else
- irq_mask = INT_EN_MASK;
-
- /* Disable timeout for erases */
- if (cmd->opcode == MMC_ERASE)
- irq_mask &= ~DTO_ENABLE;
-
- OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
- OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
- OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
-}
-
-static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
-{
- OMAP_HSMMC_WRITE(host->base, ISE, 0);
- OMAP_HSMMC_WRITE(host->base, IE, 0);
- OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
-}
-
#ifdef CONFIG_PM
/*
@@ -650,7 +625,9 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
&& time_before(jiffies, timeout))
;
- omap_hsmmc_disable_irq(host);
+ OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+ OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
+ OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
/* Do not initialize card-specific things if the power is off */
if (host->power_mode == MMC_POWER_OFF)
@@ -753,8 +730,6 @@ static void send_init_stream(struct omap_hsmmc_host *host)
return;
disable_irq(host->irq);
-
- OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
OMAP_HSMMC_WRITE(host->base, CON,
OMAP_HSMMC_READ(host->base, CON) | INIT_STREAM);
OMAP_HSMMC_WRITE(host->base, CMD, INIT_STREAM_CMD);
@@ -820,7 +795,17 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
mmc_hostname(host->mmc), cmd->opcode, cmd->arg);
host->cmd = cmd;
- omap_hsmmc_enable_irq(host, cmd);
+ /*
+ * Clear status bits and enable interrupts
+ */
+ OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+ OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
+
+ if (host->use_dma)
+ OMAP_HSMMC_WRITE(host->base, IE,
+ INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE));
+ else
+ OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
host->response_busy = 0;
if (cmd->flags & MMC_RSP_PRESENT) {
@@ -854,7 +839,13 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
if (host->use_dma)
cmdreg |= DMA_EN;
- host->req_in_progress = 1;
+ /*
+ * In an interrupt context (i.e. STOP command), the spinlock is unlocked
+ * by the interrupt handler, otherwise (i.e. for a new request) it is
+ * unlocked here.
+ */
+ if (!in_interrupt())
+ spin_unlock_irqrestore(&host->irq_lock, host->flags);
OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
@@ -869,23 +860,6 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)
return DMA_FROM_DEVICE;
}
-static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
-{
- int dma_ch;
-
- spin_lock(&host->irq_lock);
- host->req_in_progress = 0;
- dma_ch = host->dma_ch;
- spin_unlock(&host->irq_lock);
-
- 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)
- return;
- host->mrq = NULL;
- mmc_request_done(host->mmc, mrq);
-}
-
/*
* Notify the transfer complete to MMC core
*/
@@ -902,19 +876,25 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
return;
}
- omap_hsmmc_request_done(host, mrq);
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
return;
}
host->data = NULL;
+ if (host->use_dma && host->dma_ch != -1)
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
+ omap_hsmmc_get_dma_dir(host, data));
+
if (!data->error)
data->bytes_xfered += data->blocks * (data->blksz);
else
data->bytes_xfered = 0;
if (!data->stop) {
- omap_hsmmc_request_done(host, data->mrq);
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, data->mrq);
return;
}
omap_hsmmc_start_command(host, data->stop, NULL);
@@ -940,8 +920,10 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10);
}
}
- if ((host->data == NULL && !host->response_busy) || cmd->error)
- omap_hsmmc_request_done(host, cmd->mrq);
+ if ((host->data == NULL && !host->response_busy) || cmd->error) {
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, cmd->mrq);
+ }
}
/*
@@ -949,19 +931,14 @@ 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;
-
host->data->error = errno;
- spin_lock(&host->irq_lock);
- dma_ch = host->dma_ch;
- host->dma_ch = -1;
- spin_unlock(&host->irq_lock);
-
- if (host->use_dma && dma_ch != -1) {
+ if (host->use_dma && host->dma_ch != -1) {
dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
omap_hsmmc_get_dma_dir(host, host->data));
- omap_free_dma(dma_ch);
+ omap_free_dma(host->dma_ch);
+ host->dma_ch = -1;
+ up(&host->sem);
}
host->data = NULL;
}
@@ -1034,21 +1011,28 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
__func__);
}
-static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
+/*
+ * MMC controller IRQ handler
+ */
+static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
{
+ struct omap_hsmmc_host *host = dev_id;
struct mmc_data *data;
- int end_cmd = 0, end_trans = 0;
-
- if (!host->req_in_progress) {
- do {
- OMAP_HSMMC_WRITE(host->base, STAT, status);
- /* Flush posted write */
- status = OMAP_HSMMC_READ(host->base, STAT);
- } while (status & INT_EN_MASK);
- return;
+ int end_cmd = 0, end_trans = 0, status;
+
+ spin_lock(&host->irq_lock);
+
+ if (host->mrq == NULL) {
+ OMAP_HSMMC_WRITE(host->base, STAT,
+ OMAP_HSMMC_READ(host->base, STAT));
+ /* Flush posted write */
+ OMAP_HSMMC_READ(host->base, STAT);
+ spin_unlock(&host->irq_lock);
+ return IRQ_HANDLED;
}
data = host->data;
+ status = OMAP_HSMMC_READ(host->base, STAT);
dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
if (status & ERR) {
@@ -1101,27 +1085,15 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
}
OMAP_HSMMC_WRITE(host->base, STAT, status);
+ /* Flush posted write */
+ OMAP_HSMMC_READ(host->base, STAT);
if (end_cmd || ((status & CC) && host->cmd))
omap_hsmmc_cmd_done(host, host->cmd);
if ((end_trans || (status & TC)) && host->mrq)
omap_hsmmc_xfer_done(host, data);
-}
-
-/*
- * MMC controller IRQ handler
- */
-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);
- do {
- omap_hsmmc_do_irq(host, status);
- /* Flush posted write */
- status = OMAP_HSMMC_READ(host->base, STAT);
- } while (status & INT_EN_MASK);
+ spin_unlock(&host->irq_lock);
return IRQ_HANDLED;
}
@@ -1316,11 +1288,9 @@ static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
/*
* DMA call back function
*/
-static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
+static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *data)
{
- struct omap_hsmmc_host *host = cb_data;
- struct mmc_data *data = host->mrq->data;
- int dma_ch, req_in_progress;
+ struct omap_hsmmc_host *host = data;
if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
@@ -1328,38 +1298,24 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
return;
}
- spin_lock(&host->irq_lock);
- if (host->dma_ch < 0) {
- spin_unlock(&host->irq_lock);
+ if (host->dma_ch < 0)
return;
- }
host->dma_sg_idx++;
if (host->dma_sg_idx < host->dma_len) {
/* Fire up the next transfer. */
- omap_hsmmc_config_dma_params(host, data,
- data->sg + host->dma_sg_idx);
- spin_unlock(&host->irq_lock);
+ omap_hsmmc_config_dma_params(host, host->data,
+ host->data->sg + host->dma_sg_idx);
return;
}
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
- omap_hsmmc_get_dma_dir(host, data));
-
- req_in_progress = host->req_in_progress;
- dma_ch = host->dma_ch;
+ omap_free_dma(host->dma_ch);
host->dma_ch = -1;
- spin_unlock(&host->irq_lock);
-
- omap_free_dma(dma_ch);
-
- /* If DMA has finished after TC, complete the request */
- if (!req_in_progress) {
- struct mmc_request *mrq = host->mrq;
-
- host->mrq = NULL;
- mmc_request_done(host->mmc, mrq);
- }
+ /*
+ * DMA Callback: run in interrupt context.
+ * mutex_unlock will throw a kernel warning if used.
+ */
+ up(&host->sem);
}
/*
@@ -1368,7 +1324,7 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
struct mmc_request *req)
{
- int dma_ch = 0, ret = 0, i;
+ int dma_ch = 0, ret = 0, err = 1, i;
struct mmc_data *data = req->data;
/* Sanity check: all the SG entries must be aligned by block size. */
@@ -1385,7 +1341,23 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
*/
return -EINVAL;
- BUG_ON(host->dma_ch != -1);
+ /*
+ * If for some reason the DMA transfer is still active,
+ * we wait for timeout period and free the dma
+ */
+ if (host->dma_ch != -1) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(100);
+ if (down_trylock(&host->sem)) {
+ omap_free_dma(host->dma_ch);
+ host->dma_ch = -1;
+ up(&host->sem);
+ return err;
+ }
+ } else {
+ if (down_trylock(&host->sem))
+ return err;
+ }
ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
"MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
@@ -1485,27 +1457,37 @@ 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);
- if (host->protect_card) {
- if (host->reqs_blocked < 3) {
- /*
- * Ensure the controller is left in a consistent
- * state by resetting the command and data state
- * machines.
- */
- omap_hsmmc_reset_controller_fsm(host, SRD);
- omap_hsmmc_reset_controller_fsm(host, SRC);
- host->reqs_blocked += 1;
- }
- req->cmd->error = -EBADF;
- if (req->data)
- req->data->error = -EBADF;
- req->cmd->retries = 0;
- mmc_request_done(mmc, req);
- return;
- } else if (host->reqs_blocked)
- host->reqs_blocked = 0;
+ /*
+ * Prevent races with the interrupt handler because of unexpected
+ * interrupts, but not if we are already in interrupt context i.e.
+ * retries.
+ */
+ if (!in_interrupt()) {
+ spin_lock_irqsave(&host->irq_lock, host->flags);
+ /*
+ * Protect the card from I/O if there is a possibility
+ * it can be removed.
+ */
+ if (host->protect_card) {
+ if (host->reqs_blocked < 3) {
+ /*
+ * Ensure the controller is left in a consistent
+ * state by resetting the command and data state
+ * machines.
+ */
+ omap_hsmmc_reset_controller_fsm(host, SRD);
+ omap_hsmmc_reset_controller_fsm(host, SRC);
+ host->reqs_blocked += 1;
+ }
+ req->cmd->error = -EBADF;
+ if (req->data)
+ req->data->error = -EBADF;
+ spin_unlock_irqrestore(&host->irq_lock, host->flags);
+ mmc_request_done(mmc, req);
+ return;
+ } else if (host->reqs_blocked)
+ host->reqs_blocked = 0;
+ }
WARN_ON(host->mrq != NULL);
host->mrq = req;
err = omap_hsmmc_prepare_data(host, req);
@@ -1514,6 +1496,8 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
if (req->data)
req->data->error = err;
host->mrq = NULL;
+ if (!in_interrupt())
+ spin_unlock_irqrestore(&host->irq_lock, host->flags);
mmc_request_done(mmc, req);
return;
}
@@ -2093,6 +2077,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
mmc->f_min = 400000;
mmc->f_max = 52000000;
+ sema_init(&host->sem, 1);
spin_lock_init(&host->irq_lock);
host->iclk = clk_get(&pdev->dev, "ick");
@@ -2235,7 +2220,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
pdata->resume = omap_hsmmc_resume_cdirq;
}
- omap_hsmmc_disable_irq(host);
+ OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
+ OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
mmc_host_lazy_disable(host->mmc);
@@ -2356,7 +2342,10 @@ static int omap_hsmmc_suspend(struct device *dev)
ret = mmc_suspend_host(host->mmc);
mmc_host_enable(host->mmc);
if (ret == 0) {
- omap_hsmmc_disable_irq(host);
+ OMAP_HSMMC_WRITE(host->base, ISE, 0);
+ OMAP_HSMMC_WRITE(host->base, IE, 0);
+
+
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
mmc_host_disable(host->mmc);
--
1.6.6.1