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