Commit 3b0beafc authored by Guennadi Liakhovetski's avatar Guennadi Liakhovetski Committed by Chris Ball

mmc: sh_mmcif: protect against a theoretical race

The MMC subsystem does not guarantee that host driver .request() and
.set_ios() callbacks are serialised. Such concurrent calls, however,
do not have to be meaningfully supported, drivers just have to make
sure to avoid any severe problems.
Signed-off-by: default avatarGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Cc: Simon Horman <horms@verge.net.au>
Cc: Magnus Damm <damm@opensource.se>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent 06e8935f
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/mmc/sh_mmcif.h> #include <linux/mmc/sh_mmcif.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h>
#define DRIVER_NAME "sh_mmcif" #define DRIVER_NAME "sh_mmcif"
#define DRIVER_VERSION "2010-04-28" #define DRIVER_VERSION "2010-04-28"
...@@ -153,6 +154,12 @@ ...@@ -153,6 +154,12 @@
#define CLKDEV_MMC_DATA 20000000 /* 20MHz */ #define CLKDEV_MMC_DATA 20000000 /* 20MHz */
#define CLKDEV_INIT 400000 /* 400 KHz */ #define CLKDEV_INIT 400000 /* 400 KHz */
enum mmcif_state {
STATE_IDLE,
STATE_REQUEST,
STATE_IOS,
};
struct sh_mmcif_host { struct sh_mmcif_host {
struct mmc_host *mmc; struct mmc_host *mmc;
struct mmc_data *data; struct mmc_data *data;
...@@ -164,6 +171,8 @@ struct sh_mmcif_host { ...@@ -164,6 +171,8 @@ struct sh_mmcif_host {
long timeout; long timeout;
void __iomem *addr; void __iomem *addr;
struct completion intr_wait; struct completion intr_wait;
enum mmcif_state state;
spinlock_t lock;
/* DMA support */ /* DMA support */
struct dma_chan *chan_rx; struct dma_chan *chan_rx;
...@@ -798,17 +807,31 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, ...@@ -798,17 +807,31 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
{ {
struct sh_mmcif_host *host = mmc_priv(mmc); struct sh_mmcif_host *host = mmc_priv(mmc);
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
if (host->state != STATE_IDLE) {
spin_unlock_irqrestore(&host->lock, flags);
mrq->cmd->error = -EAGAIN;
mmc_request_done(mmc, mrq);
return;
}
host->state = STATE_REQUEST;
spin_unlock_irqrestore(&host->lock, flags);
switch (mrq->cmd->opcode) { switch (mrq->cmd->opcode) {
/* MMCIF does not support SD/SDIO command */ /* MMCIF does not support SD/SDIO command */
case SD_IO_SEND_OP_COND: case SD_IO_SEND_OP_COND:
case MMC_APP_CMD: case MMC_APP_CMD:
host->state = STATE_IDLE;
mrq->cmd->error = -ETIMEDOUT; mrq->cmd->error = -ETIMEDOUT;
mmc_request_done(mmc, mrq); mmc_request_done(mmc, mrq);
return; return;
case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */ case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */
if (!mrq->data) { if (!mrq->data) {
/* send_if_cond cmd (not support) */ /* send_if_cond cmd (not support) */
host->state = STATE_IDLE;
mrq->cmd->error = -ETIMEDOUT; mrq->cmd->error = -ETIMEDOUT;
mmc_request_done(mmc, mrq); mmc_request_done(mmc, mrq);
return; return;
...@@ -830,12 +853,9 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) ...@@ -830,12 +853,9 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
sh_mmcif_start_cmd(host, mrq, mrq->cmd); sh_mmcif_start_cmd(host, mrq, mrq->cmd);
host->data = NULL; host->data = NULL;
if (mrq->cmd->error != 0) { if (!mrq->cmd->error && mrq->stop)
mmc_request_done(mmc, mrq);
return;
}
if (mrq->stop)
sh_mmcif_stop_cmd(host, mrq, mrq->stop); sh_mmcif_stop_cmd(host, mrq, mrq->stop);
host->state = STATE_IDLE;
mmc_request_done(mmc, mrq); mmc_request_done(mmc, mrq);
} }
...@@ -843,6 +863,16 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -843,6 +863,16 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{ {
struct sh_mmcif_host *host = mmc_priv(mmc); struct sh_mmcif_host *host = mmc_priv(mmc);
struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
if (host->state != STATE_IDLE) {
spin_unlock_irqrestore(&host->lock, flags);
return;
}
host->state = STATE_IOS;
spin_unlock_irqrestore(&host->lock, flags);
if (ios->power_mode == MMC_POWER_UP) { if (ios->power_mode == MMC_POWER_UP) {
if (p->set_pwr) if (p->set_pwr)
...@@ -852,6 +882,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -852,6 +882,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
sh_mmcif_clock_control(host, 0); sh_mmcif_clock_control(host, 0);
if (ios->power_mode == MMC_POWER_OFF && p->down_pwr) if (ios->power_mode == MMC_POWER_OFF && p->down_pwr)
p->down_pwr(host->pd); p->down_pwr(host->pd);
host->state = STATE_IDLE;
return; return;
} }
...@@ -859,6 +890,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -859,6 +890,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
sh_mmcif_clock_control(host, ios->clock); sh_mmcif_clock_control(host, ios->clock);
host->bus_width = ios->bus_width; host->bus_width = ios->bus_width;
host->state = STATE_IDLE;
} }
static int sh_mmcif_get_cd(struct mmc_host *mmc) static int sh_mmcif_get_cd(struct mmc_host *mmc)
...@@ -996,6 +1028,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) ...@@ -996,6 +1028,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
host->pd = pdev; host->pd = pdev;
init_completion(&host->intr_wait); init_completion(&host->intr_wait);
spin_lock_init(&host->lock);
mmc->ops = &sh_mmcif_ops; mmc->ops = &sh_mmcif_ops;
mmc->f_max = host->clk; mmc->f_max = host->clk;
...@@ -1025,6 +1058,8 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) ...@@ -1025,6 +1058,8 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
mmc_add_host(mmc); mmc_add_host(mmc);
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host); ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host);
if (ret) { if (ret) {
dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n"); dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n");
...@@ -1037,7 +1072,6 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) ...@@ -1037,7 +1072,6 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
goto clean_up2; goto clean_up2;
} }
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
sh_mmcif_detect(host->mmc); sh_mmcif_detect(host->mmc);
dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION); dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION);
...@@ -1063,11 +1097,11 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) ...@@ -1063,11 +1097,11 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
mmc_remove_host(host->mmc); mmc_remove_host(host->mmc);
sh_mmcif_release_dma(host); sh_mmcif_release_dma(host);
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
if (host->addr) if (host->addr)
iounmap(host->addr); iounmap(host->addr);
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment