mmc: sh_mmcif: switch to completion, fix flags

In sh_mmcif.c an event is used as a completion, switch over. When a
wait_for_completion*_timeout() returns, it suffices to check the remaining
time, setting an additional flag before waking up the waiting task only reduces
the race window, but does not eliminate it. This patch switches the driver to
use a completion to signal an interrupt, the only case, when an interrupt
should not wake up the waiter, is when an automatic CMD12 completes. Also fix
MODULE_ALIAS.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
Guennadi Liakhovetski 2010-11-24 10:05:12 +00:00 committed by Paul Mundt
parent 2d3e4e7652
commit aa0787a90c
1 changed files with 46 additions and 71 deletions

View File

@ -16,6 +16,8 @@
*
*/
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
@ -24,7 +26,6 @@
#include <linux/mmc/sdio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/mmc/sh_mmcif.h>
#define DRIVER_NAME "sh_mmcif"
@ -158,11 +159,10 @@ struct sh_mmcif_host {
struct clk *hclk;
unsigned int clk;
int bus_width;
u16 wait_int;
u16 sd_error;
bool sd_error;
long timeout;
void __iomem *addr;
wait_queue_head_t intr_wait;
struct completion intr_wait;
};
@ -216,8 +216,7 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
u32 state1, state2;
int ret, timeout = 10000000;
host->sd_error = 0;
host->wait_int = 0;
host->sd_error = false;
state1 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1);
state2 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS2);
@ -264,17 +263,13 @@ static int sh_mmcif_single_read(struct sh_mmcif_host *host,
long time;
u32 blocksize, i, *p = sg_virt(data->sg);
host->wait_int = 0;
/* buf read enable */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
time = wait_event_interruptible_timeout(host->intr_wait,
host->wait_int == 1 ||
host->sd_error == 1, host->timeout);
if (host->wait_int != 1 && (time == 0 || host->sd_error != 0))
time = wait_for_completion_interruptible_timeout(&host->intr_wait,
host->timeout);
if (time <= 0 || host->sd_error)
return sh_mmcif_error_manage(host);
host->wait_int = 0;
blocksize = (BLOCK_SIZE_MASK &
sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3;
for (i = 0; i < blocksize / 4; i++)
@ -282,13 +277,11 @@ static int sh_mmcif_single_read(struct sh_mmcif_host *host,
/* buffer read end */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
time = wait_event_interruptible_timeout(host->intr_wait,
host->wait_int == 1 ||
host->sd_error == 1, host->timeout);
if (host->wait_int != 1 && (time == 0 || host->sd_error != 0))
time = wait_for_completion_interruptible_timeout(&host->intr_wait,
host->timeout);
if (time <= 0 || host->sd_error)
return sh_mmcif_error_manage(host);
host->wait_int = 0;
return 0;
}
@ -303,19 +296,15 @@ static int sh_mmcif_multi_read(struct sh_mmcif_host *host,
MMCIF_CE_BLOCK_SET);
for (j = 0; j < data->sg_len; j++) {
p = sg_virt(data->sg);
host->wait_int = 0;
for (sec = 0; sec < data->sg->length / blocksize; sec++) {
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
/* buf read enable */
time = wait_event_interruptible_timeout(host->intr_wait,
host->wait_int == 1 ||
host->sd_error == 1, host->timeout);
time = wait_for_completion_interruptible_timeout(&host->intr_wait,
host->timeout);
if (host->wait_int != 1 &&
(time == 0 || host->sd_error != 0))
if (time <= 0 || host->sd_error)
return sh_mmcif_error_manage(host);
host->wait_int = 0;
for (i = 0; i < blocksize / 4; i++)
*p++ = sh_mmcif_readl(host->addr,
MMCIF_CE_DATA);
@ -333,17 +322,14 @@ static int sh_mmcif_single_write(struct sh_mmcif_host *host,
long time;
u32 blocksize, i, *p = sg_virt(data->sg);
host->wait_int = 0;
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
/* buf write enable */
time = wait_event_interruptible_timeout(host->intr_wait,
host->wait_int == 1 ||
host->sd_error == 1, host->timeout);
if (host->wait_int != 1 && (time == 0 || host->sd_error != 0))
time = wait_for_completion_interruptible_timeout(&host->intr_wait,
host->timeout);
if (time <= 0 || host->sd_error)
return sh_mmcif_error_manage(host);
host->wait_int = 0;
blocksize = (BLOCK_SIZE_MASK &
sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3;
for (i = 0; i < blocksize / 4; i++)
@ -352,13 +338,11 @@ static int sh_mmcif_single_write(struct sh_mmcif_host *host,
/* buffer write end */
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE);
time = wait_event_interruptible_timeout(host->intr_wait,
host->wait_int == 1 ||
host->sd_error == 1, host->timeout);
if (host->wait_int != 1 && (time == 0 || host->sd_error != 0))
time = wait_for_completion_interruptible_timeout(&host->intr_wait,
host->timeout);
if (time <= 0 || host->sd_error)
return sh_mmcif_error_manage(host);
host->wait_int = 0;
return 0;
}
@ -374,19 +358,15 @@ static int sh_mmcif_multi_write(struct sh_mmcif_host *host,
for (j = 0; j < data->sg_len; j++) {
p = sg_virt(data->sg);
host->wait_int = 0;
for (sec = 0; sec < data->sg->length / blocksize; sec++) {
sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
/* buf write enable*/
time = wait_event_interruptible_timeout(host->intr_wait,
host->wait_int == 1 ||
host->sd_error == 1, host->timeout);
time = wait_for_completion_interruptible_timeout(&host->intr_wait,
host->timeout);
if (host->wait_int != 1 &&
(time == 0 || host->sd_error != 0))
if (time <= 0 || host->sd_error)
return sh_mmcif_error_manage(host);
host->wait_int = 0;
for (i = 0; i < blocksize / 4; i++)
sh_mmcif_writel(host->addr,
MMCIF_CE_DATA, *p++);
@ -556,13 +536,12 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, mask);
/* set arg */
sh_mmcif_writel(host->addr, MMCIF_CE_ARG, cmd->arg);
host->wait_int = 0;
/* set cmd */
sh_mmcif_writel(host->addr, MMCIF_CE_CMD_SET, opc);
time = wait_event_interruptible_timeout(host->intr_wait,
host->wait_int == 1 || host->sd_error == 1, host->timeout);
if (host->wait_int != 1 && time == 0) {
time = wait_for_completion_interruptible_timeout(&host->intr_wait,
host->timeout);
if (time <= 0) {
cmd->error = sh_mmcif_error_manage(host);
return;
}
@ -579,19 +558,14 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
cmd->error = sh_mmcif_error_manage(host);
break;
}
host->sd_error = 0;
host->wait_int = 0;
host->sd_error = false;
return;
}
if (!(cmd->flags & MMC_RSP_PRESENT)) {
cmd->error = ret;
host->wait_int = 0;
return;
}
if (host->wait_int == 1) {
sh_mmcif_get_response(host, cmd);
host->wait_int = 0;
}
sh_mmcif_get_response(host, cmd);
if (host->data) {
ret = sh_mmcif_data_trans(host, mrq, cmd->opcode);
if (ret < 0)
@ -618,15 +592,13 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
return;
}
time = wait_event_interruptible_timeout(host->intr_wait,
host->wait_int == 1 ||
host->sd_error == 1, host->timeout);
if (host->wait_int != 1 && (time == 0 || host->sd_error != 0)) {
time = wait_for_completion_interruptible_timeout(&host->intr_wait,
host->timeout);
if (time <= 0 || host->sd_error) {
cmd->error = sh_mmcif_error_manage(host);
return;
}
sh_mmcif_get_cmd12response(host, cmd);
host->wait_int = 0;
cmd->error = 0;
}
@ -712,7 +684,7 @@ static void sh_mmcif_detect(struct mmc_host *mmc)
static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
{
struct sh_mmcif_host *host = dev_id;
u32 state = 0;
u32 state;
int err = 0;
state = sh_mmcif_readl(host->addr, MMCIF_CE_INT);
@ -757,11 +729,13 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
err = 1;
}
if (err) {
host->sd_error = 1;
host->sd_error = true;
pr_debug("%s: int err state = %08x\n", DRIVER_NAME, state);
}
host->wait_int = 1;
wake_up(&host->intr_wait);
if (state & ~(INT_CMD12RBE | INT_CMD12CRE))
complete(&host->intr_wait);
else
dev_dbg(&host->pd->dev, "Unexpected IRQ 0x%x\n", state);
return IRQ_HANDLED;
}
@ -819,7 +793,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
host->clk = clk_get_rate(host->hclk);
host->pd = pdev;
init_waitqueue_head(&host->intr_wait);
init_completion(&host->intr_wait);
mmc->ops = &sh_mmcif_ops;
mmc->f_max = host->clk;
@ -880,20 +854,21 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
int irq[2];
mmc_remove_host(host->mmc);
if (host->addr)
iounmap(host->addr);
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
irq[0] = platform_get_irq(pdev, 0);
irq[1] = platform_get_irq(pdev, 1);
if (host->addr)
iounmap(host->addr);
platform_set_drvdata(pdev, NULL);
mmc_remove_host(host->mmc);
free_irq(irq[0], host);
free_irq(irq[1], host);
platform_set_drvdata(pdev, NULL);
clk_disable(host->hclk);
mmc_free_host(host->mmc);
@ -924,5 +899,5 @@ module_exit(sh_mmcif_exit);
MODULE_DESCRIPTION("SuperH on-chip MMC/eMMC interface driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS(DRIVER_NAME);
MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_AUTHOR("Yusuke Goda <yusuke.goda.sx@renesas.com>");