MMC: OMAP: Move failing command abortion to workqueue
Abort failed command from workqueue rather than from an interrupt, allowing longer delays in abortion. Signed-off-by: Jarkko Lavinen <jarkko.lavinen@nokia.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
This commit is contained in:
parent
7584d276d4
commit
0fb4723d40
|
@ -134,8 +134,9 @@ struct mmc_omap_host {
|
||||||
unsigned char bus_mode;
|
unsigned char bus_mode;
|
||||||
unsigned char hw_bus_mode;
|
unsigned char hw_bus_mode;
|
||||||
|
|
||||||
struct work_struct cmd_abort;
|
struct work_struct cmd_abort_work;
|
||||||
struct timer_list cmd_timer;
|
unsigned abort:1;
|
||||||
|
struct timer_list cmd_abort_timer;
|
||||||
|
|
||||||
unsigned int sg_len;
|
unsigned int sg_len;
|
||||||
int sg_idx;
|
int sg_idx;
|
||||||
|
@ -320,7 +321,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
|
||||||
if (host->data && !(host->data->flags & MMC_DATA_WRITE))
|
if (host->data && !(host->data->flags & MMC_DATA_WRITE))
|
||||||
cmdreg |= 1 << 15;
|
cmdreg |= 1 << 15;
|
||||||
|
|
||||||
mod_timer(&host->cmd_timer, jiffies + HZ/2);
|
mod_timer(&host->cmd_abort_timer, jiffies + HZ/2);
|
||||||
|
|
||||||
OMAP_MMC_WRITE(host, CTO, 200);
|
OMAP_MMC_WRITE(host, CTO, 200);
|
||||||
OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
|
OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
|
||||||
|
@ -381,7 +382,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mmc_omap_send_abort(struct mmc_omap_host *host)
|
mmc_omap_send_abort(struct mmc_omap_host *host, int maxloops)
|
||||||
{
|
{
|
||||||
struct mmc_omap_slot *slot = host->current_slot;
|
struct mmc_omap_slot *slot = host->current_slot;
|
||||||
unsigned int restarts, passes, timeout;
|
unsigned int restarts, passes, timeout;
|
||||||
|
@ -390,7 +391,7 @@ mmc_omap_send_abort(struct mmc_omap_host *host)
|
||||||
/* Sending abort takes 80 clocks. Have some extra and round up */
|
/* Sending abort takes 80 clocks. Have some extra and round up */
|
||||||
timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq;
|
timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq;
|
||||||
restarts = 0;
|
restarts = 0;
|
||||||
while (restarts < 10000) {
|
while (restarts < maxloops) {
|
||||||
OMAP_MMC_WRITE(host, STAT, 0xFFFF);
|
OMAP_MMC_WRITE(host, STAT, 0xFFFF);
|
||||||
OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7));
|
OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7));
|
||||||
|
|
||||||
|
@ -412,18 +413,13 @@ out:
|
||||||
static void
|
static void
|
||||||
mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data)
|
mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data)
|
||||||
{
|
{
|
||||||
u16 ie;
|
|
||||||
|
|
||||||
if (host->dma_in_use)
|
if (host->dma_in_use)
|
||||||
mmc_omap_release_dma(host, data, 1);
|
mmc_omap_release_dma(host, data, 1);
|
||||||
|
|
||||||
host->data = NULL;
|
host->data = NULL;
|
||||||
host->sg_len = 0;
|
host->sg_len = 0;
|
||||||
|
|
||||||
ie = OMAP_MMC_READ(host, IE);
|
mmc_omap_send_abort(host, 10000);
|
||||||
OMAP_MMC_WRITE(host, IE, 0);
|
|
||||||
OMAP_MMC_WRITE(host, IE, ie);
|
|
||||||
mmc_omap_send_abort(host);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -479,7 +475,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
|
||||||
{
|
{
|
||||||
host->cmd = NULL;
|
host->cmd = NULL;
|
||||||
|
|
||||||
del_timer(&host->cmd_timer);
|
del_timer(&host->cmd_abort_timer);
|
||||||
|
|
||||||
if (cmd->flags & MMC_RSP_PRESENT) {
|
if (cmd->flags & MMC_RSP_PRESENT) {
|
||||||
if (cmd->flags & MMC_RSP_136) {
|
if (cmd->flags & MMC_RSP_136) {
|
||||||
|
@ -523,38 +519,48 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
|
||||||
static void mmc_omap_abort_command(struct work_struct *work)
|
static void mmc_omap_abort_command(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
|
struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
|
||||||
cmd_abort);
|
cmd_abort_work);
|
||||||
u16 ie;
|
BUG_ON(!host->cmd);
|
||||||
|
|
||||||
ie = OMAP_MMC_READ(host, IE);
|
|
||||||
OMAP_MMC_WRITE(host, IE, 0);
|
|
||||||
|
|
||||||
if (!host->cmd) {
|
|
||||||
OMAP_MMC_WRITE(host, IE, ie);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_dbg(mmc_dev(host->mmc), "Aborting stuck command CMD%d\n",
|
dev_dbg(mmc_dev(host->mmc), "Aborting stuck command CMD%d\n",
|
||||||
host->cmd->opcode);
|
host->cmd->opcode);
|
||||||
|
|
||||||
if (host->data && host->dma_in_use)
|
if (host->cmd->error == 0)
|
||||||
mmc_omap_release_dma(host, host->data, 1);
|
host->cmd->error = -ETIMEDOUT;
|
||||||
|
|
||||||
host->data = NULL;
|
if (host->data == NULL) {
|
||||||
host->sg_len = 0;
|
struct mmc_command *cmd;
|
||||||
|
struct mmc_host *mmc;
|
||||||
|
|
||||||
mmc_omap_send_abort(host);
|
cmd = host->cmd;
|
||||||
host->cmd->error = -ETIMEDOUT;
|
host->cmd = NULL;
|
||||||
mmc_omap_cmd_done(host, host->cmd);
|
mmc_omap_send_abort(host, 10000);
|
||||||
OMAP_MMC_WRITE(host, IE, ie);
|
|
||||||
|
host->mrq = NULL;
|
||||||
|
mmc = host->mmc;
|
||||||
|
mmc_omap_release_slot(host->current_slot);
|
||||||
|
mmc_request_done(mmc, cmd->mrq);
|
||||||
|
} else
|
||||||
|
mmc_omap_cmd_done(host, host->cmd);
|
||||||
|
|
||||||
|
host->abort = 0;
|
||||||
|
enable_irq(host->irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mmc_omap_cmd_timer(unsigned long data)
|
mmc_omap_cmd_timer(unsigned long data)
|
||||||
{
|
{
|
||||||
struct mmc_omap_host *host = (struct mmc_omap_host *) data;
|
struct mmc_omap_host *host = (struct mmc_omap_host *) data;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
schedule_work(&host->cmd_abort);
|
spin_lock_irqsave(&host->slot_lock, flags);
|
||||||
|
if (host->cmd != NULL && !host->abort) {
|
||||||
|
OMAP_MMC_WRITE(host, IE, 0);
|
||||||
|
disable_irq(host->irq);
|
||||||
|
host->abort = 1;
|
||||||
|
schedule_work(&host->cmd_abort_work);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&host->slot_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PIO only */
|
/* PIO only */
|
||||||
|
@ -728,6 +734,15 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cmd_error && host->data) {
|
||||||
|
del_timer(&host->cmd_abort_timer);
|
||||||
|
host->abort = 1;
|
||||||
|
OMAP_MMC_WRITE(host, IE, 0);
|
||||||
|
disable_irq(host->irq);
|
||||||
|
schedule_work(&host->cmd_abort_work);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
if (end_command)
|
if (end_command)
|
||||||
mmc_omap_cmd_done(host, host->cmd);
|
mmc_omap_cmd_done(host, host->cmd);
|
||||||
if (host->data != NULL) {
|
if (host->data != NULL) {
|
||||||
|
@ -1316,8 +1331,9 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
|
||||||
goto err_free_mem_region;
|
goto err_free_mem_region;
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_WORK(&host->cmd_abort, mmc_omap_abort_command);
|
INIT_WORK(&host->cmd_abort_work, mmc_omap_abort_command);
|
||||||
setup_timer(&host->cmd_timer, mmc_omap_cmd_timer, (unsigned long) host);
|
setup_timer(&host->cmd_abort_timer, mmc_omap_cmd_timer,
|
||||||
|
(unsigned long) host);
|
||||||
|
|
||||||
spin_lock_init(&host->dma_lock);
|
spin_lock_init(&host->dma_lock);
|
||||||
setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host);
|
setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host);
|
||||||
|
|
Loading…
Reference in New Issue