diff options
Diffstat (limited to 'recipes-kernel/linux/linux-omap-2.6.39/sakoman/0011-Revert-omap_hsmmc-improve-interrupt-synchronisation.patch')
-rw-r--r-- | recipes-kernel/linux/linux-omap-2.6.39/sakoman/0011-Revert-omap_hsmmc-improve-interrupt-synchronisation.patch | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-omap-2.6.39/sakoman/0011-Revert-omap_hsmmc-improve-interrupt-synchronisation.patch b/recipes-kernel/linux/linux-omap-2.6.39/sakoman/0011-Revert-omap_hsmmc-improve-interrupt-synchronisation.patch new file mode 100644 index 0000000000..7592bab28f --- /dev/null +++ b/recipes-kernel/linux/linux-omap-2.6.39/sakoman/0011-Revert-omap_hsmmc-improve-interrupt-synchronisation.patch @@ -0,0 +1,482 @@ +From ed9bca69fdaa7edac3573e5ef3f011c7f7dc4898 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/28] 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 + |