|
@@ -56,6 +56,7 @@
|
|
|
#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>
|
|
@@ -196,6 +197,7 @@ enum mmcif_state {
|
|
|
STATE_IDLE,
|
|
|
STATE_REQUEST,
|
|
|
STATE_IOS,
|
|
|
+ STATE_TIMEOUT,
|
|
|
};
|
|
|
|
|
|
enum mmcif_wait_for {
|
|
@@ -232,6 +234,7 @@ struct sh_mmcif_host {
|
|
|
int sg_blkidx;
|
|
|
bool power;
|
|
|
bool card_present;
|
|
|
+ struct mutex thread_lock;
|
|
|
|
|
|
/* DMA support */
|
|
|
struct dma_chan *chan_rx;
|
|
@@ -255,11 +258,11 @@ static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host,
|
|
|
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;
|
|
|
|
|
@@ -1113,11 +1116,21 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
|
|
|
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
|
|
@@ -1125,6 +1138,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
|
|
|
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? */
|
|
@@ -1166,6 +1180,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
|
|
|
if (wait) {
|
|
|
schedule_delayed_work(&host->timeout_work, host->timeout);
|
|
|
/* Wait for more data */
|
|
|
+ mutex_unlock(&host->thread_lock);
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
@@ -1179,6 +1194,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
|
|
|
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;
|
|
|
}
|
|
|
}
|
|
@@ -1189,6 +1205,8 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
|
|
|
host->mrq = NULL;
|
|
|
mmc_request_done(host->mmc, mrq);
|
|
|
|
|
|
+ mutex_unlock(&host->thread_lock);
|
|
|
+
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
@@ -1262,11 +1280,24 @@ static void mmcif_timeout_work(struct work_struct *work)
|
|
|
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
|
|
@@ -1410,6 +1441,8 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
|
|
goto erqcd;
|
|
|
}
|
|
|
|
|
|
+ mutex_init(&host->thread_lock);
|
|
|
+
|
|
|
clk_disable(host->hclk);
|
|
|
ret = mmc_add_host(mmc);
|
|
|
if (ret < 0)
|