#include <linux/mmc/sh_mmcif.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
STATE_IDLE,
STATE_REQUEST,
STATE_IOS,
+ STATE_TIMEOUT,
};
enum mmcif_wait_for {
int sg_blkidx;
bool power;
bool card_present;
+ struct mutex thread_lock;
/* DMA support */
struct dma_chan *chan_rx;
static void mmcif_dma_complete(void *arg)
{
struct sh_mmcif_host *host = arg;
- struct mmc_data *data = host->mrq->data;
+ struct mmc_request *mrq = host->mrq;
dev_dbg(&host->pd->dev, "Command completed\n");
- if (WARN(!data, "%s: NULL data in DMA completion!\n",
+ if (WARN(!mrq || !mrq->data, "%s: NULL data in DMA completion!\n",
dev_name(&host->pd->dev)))
return;
static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
{
struct sh_mmcif_host *host = dev_id;
- struct mmc_request *mrq = host->mrq;
+ struct mmc_request *mrq;
bool wait = false;
cancel_delayed_work_sync(&host->timeout_work);
+ mutex_lock(&host->thread_lock);
+
+ mrq = host->mrq;
+ if (!mrq) {
+ dev_dbg(&host->pd->dev, "IRQ thread state %u, wait %u: NULL mrq!\n",
+ host->state, host->wait_for);
+ mutex_unlock(&host->thread_lock);
+ return IRQ_HANDLED;
+ }
+
/*
* All handlers return true, if processing continues, and false, if the
* request has to be completed - successfully or not
switch (host->wait_for) {
case MMCIF_WAIT_FOR_REQUEST:
/* We're too late, the timeout has already kicked in */
+ mutex_unlock(&host->thread_lock);
return IRQ_HANDLED;
case MMCIF_WAIT_FOR_CMD:
/* Wait for data? */
if (wait) {
schedule_delayed_work(&host->timeout_work, host->timeout);
/* Wait for more data */
+ mutex_unlock(&host->thread_lock);
return IRQ_HANDLED;
}
sh_mmcif_stop_cmd(host, mrq);
if (!mrq->stop->error) {
schedule_delayed_work(&host->timeout_work, host->timeout);
+ mutex_unlock(&host->thread_lock);
return IRQ_HANDLED;
}
}
host->mrq = NULL;
mmc_request_done(host->mmc, mrq);
+ mutex_unlock(&host->thread_lock);
+
return IRQ_HANDLED;
}
struct delayed_work *d = container_of(work, struct delayed_work, work);
struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work);
struct mmc_request *mrq = host->mrq;
+ unsigned long flags;
if (host->dying)
/* Don't run after mmc_remove_host() */
return;
+ dev_dbg(&host->pd->dev, "Timeout waiting for %u, opcode %u\n",
+ host->wait_for, mrq->cmd->opcode);
+
+ spin_lock_irqsave(&host->lock, flags);
+ if (host->state == STATE_IDLE) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
+ host->state = STATE_TIMEOUT;
+ spin_unlock_irqrestore(&host->lock, flags);
+
/*
* Handle races with cancel_delayed_work(), unless
* cancel_delayed_work_sync() is used
goto erqcd;
}
+ mutex_init(&host->thread_lock);
+
clk_disable(host->hclk);
ret = mmc_add_host(mmc);
if (ret < 0)