Skip to content

Commit

Permalink
mmc: sh_mmcif: switch to completion, fix flags
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Guennadi Liakhovetski authored and Paul Mundt committed Nov 25, 2010
1 parent 2d3e4e7 commit aa0787a
Showing 1 changed file with 46 additions and 71 deletions.
117 changes: 46 additions & 71 deletions drivers/mmc/host/sh_mmcif.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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"
Expand Down Expand Up @@ -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;
};


Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -264,31 +263,25 @@ 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++)
*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);

/* 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;
}

Expand All @@ -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);
Expand All @@ -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++)
Expand All @@ -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;
}

Expand All @@ -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++);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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)
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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];

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);
mmc_remove_host(host->mmc);

if (host->addr)
iounmap(host->addr);

platform_set_drvdata(pdev, NULL);
mmc_remove_host(host->mmc);
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);

free_irq(irq[0], host);
free_irq(irq[1], host);

platform_set_drvdata(pdev, NULL);

clk_disable(host->hclk);
mmc_free_host(host->mmc);

Expand Down Expand Up @@ -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>");

0 comments on commit aa0787a

Please sign in to comment.