summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRichard Zhu <r65037@freescale.com>2009-09-08 16:13:33 +0800
committerRichard Zhu <r65037@freescale.com>2009-09-11 10:16:25 +0800
commitc84f40c5f5fbdd0101e6420d595f48c4b486011a (patch)
tree301ac067efefeb0333d110710dbdb7675e1af660 /drivers
parent3bc218f65afdfb22566de41652349eaf2a63229f (diff)
ENGR00116377 Support very fast plug in/out cards
SD card can't be recognized after the very quick extraction/insertion operations. Kernel dump when re-insert some kinds of cards during the suspend/resume Signed-off-by: Richard Zhu <r65037@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mmc/host/mx_sdhci.c79
-rw-r--r--drivers/mmc/host/mx_sdhci.h4
2 files changed, 67 insertions, 16 deletions
diff --git a/drivers/mmc/host/mx_sdhci.c b/drivers/mmc/host/mx_sdhci.c
index c6e3a08e221e..c6352e9edd3b 100644
--- a/drivers/mmc/host/mx_sdhci.c
+++ b/drivers/mmc/host/mx_sdhci.c
@@ -678,7 +678,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
udelay(20);
}
- mod_timer(&host->timer, jiffies + 10 * HZ);
+ mod_timer(&host->timer, jiffies + 1 * HZ);
host->cmd = cmd;
@@ -1197,7 +1197,7 @@ static void sdhci_tasklet_finish(unsigned long param)
static void sdhci_timeout_timer(unsigned long data)
{
struct sdhci_host *host;
- unsigned long flags;
+ unsigned long tmp, flags;
host = (struct sdhci_host *)data;
@@ -1219,12 +1219,32 @@ static void sdhci_timeout_timer(unsigned long data)
tasklet_schedule(&host->finish_tasklet);
}
+
+ if (!readl(host->ioaddr + SDHCI_SIGNAL_ENABLE)) {
+ printk(KERN_ERR "%s, ERROR SIG_INT is 0.\n", __func__);
+ tmp = readl(host->ioaddr + SDHCI_INT_ENABLE);
+ if (host->sdio_enable)
+ writel(tmp, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ else
+ writel(tmp & ~SDHCI_INT_CARD_INT,
+ host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ if (!host->plat_data->status(host->mmc->parent))
+ schedule_work(&host->cd_wq);
+ }
}
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
}
+static void sdhci_cd_timer(unsigned long data)
+{
+ struct sdhci_host *host;
+
+ host = (struct sdhci_host *)data;
+ schedule_work(&host->cd_wq);
+}
+
/*****************************************************************************\
* *
* Interrupt handling *
@@ -1273,6 +1293,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
"though no data operation was in progress.\n",
mmc_hostname(host->mmc), (unsigned)intmask);
sdhci_dumpregs(host);
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
return;
}
@@ -1431,22 +1453,22 @@ static void esdhc_cd_callback(struct work_struct *work)
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
}
- sdhci_init(host);
+
+ if (host->init_flag > 0)
+ /* The initialization of sdhc controller has been
+ * done in the resume func */
+ host->init_flag--;
+ else
+ sdhci_init(host);
}
spin_unlock_irqrestore(&host->lock, flags);
- mmc_detect_change(host->mmc, msecs_to_jiffies(200));
-
- if (!host->detect_irq)
- return;
- do {
- cd_status = host->plat_data->status(host->mmc->parent);
- if (cd_status)
- set_irq_type(host->detect_irq, IRQF_TRIGGER_FALLING);
- else
- set_irq_type(host->detect_irq, IRQF_TRIGGER_RISING);
- } while (cd_status != host->plat_data->status(host->mmc->parent));
+ if (host->flags & SDHCI_CD_PRESENT) {
+ del_timer(&host->cd_timer);
+ mmc_detect_change(host->mmc, msecs_to_jiffies(100));
+ } else
+ mmc_detect_change(host->mmc, 0);
}
/*!
@@ -1461,9 +1483,33 @@ static void esdhc_cd_callback(struct work_struct *work)
*/
static irqreturn_t sdhci_cd_irq(int irq, void *dev_id)
{
+ unsigned int cd_status = 0;
struct sdhci_host *host = dev_id;
- schedule_work(&host->cd_wq);
+ do {
+ if (host->detect_irq == 0)
+ break;
+ cd_status = host->plat_data->status(host->mmc->parent);
+ if (cd_status)
+ set_irq_type(host->detect_irq, IRQF_TRIGGER_FALLING);
+ else
+ set_irq_type(host->detect_irq, IRQF_TRIGGER_RISING);
+ } while (cd_status != host->plat_data->status(host->mmc->parent));
+
+ DBG("cd_status=%d %s\n", cd_status, cd_status ? "removed" : "inserted");
+
+ cd_status = host->plat_data->status(host->mmc->parent);
+ if (!cd_status)
+ /* If there is a card in the slot, the timer is start
+ * to work. Then the card detection would be carried
+ * after the timer is timeout.
+ * */
+ mod_timer(&host->cd_timer, jiffies + HZ / 2);
+ else
+ /* If there is no card, call the card detection func
+ * immediately. */
+ schedule_work(&host->cd_wq);
+
return IRQ_HANDLED;
}
@@ -1614,6 +1660,7 @@ static int sdhci_resume(struct platform_device *pdev)
if (ret)
return ret;
sdhci_init(chip->hosts[i]);
+ chip->hosts[i]->init_flag = 2;
mmiowb();
ret = mmc_resume_host(chip->hosts[i]->mmc);
if (ret)
@@ -1873,6 +1920,7 @@ static int __devinit sdhci_probe_slot(struct platform_device
INIT_WORK(&host->cd_wq, esdhc_cd_callback);
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
+ setup_timer(&host->cd_timer, sdhci_cd_timer, (unsigned long)host);
if (host->detect_irq) {
ret = request_irq(host->detect_irq, sdhci_cd_irq, 0,
@@ -1930,6 +1978,7 @@ static int __devinit sdhci_probe_slot(struct platform_device
}
out4:
del_timer_sync(&host->timer);
+ del_timer_sync(&host->cd_timer);
tasklet_kill(&host->card_tasklet);
tasklet_kill(&host->finish_tasklet);
out3:
diff --git a/drivers/mmc/host/mx_sdhci.h b/drivers/mmc/host/mx_sdhci.h
index 1185459868ac..509d444a6e81 100644
--- a/drivers/mmc/host/mx_sdhci.h
+++ b/drivers/mmc/host/mx_sdhci.h
@@ -3,7 +3,7 @@
* Controller Interface driver
*
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
- * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -213,6 +213,7 @@ struct sdhci_host {
spinlock_t lock; /* Mutex */
+ int init_flag; /* Host has been initialized */
int flags; /* Host attributes */
#define SDHCI_USE_DMA (1<<0) /* Host is DMA capable */
#define SDHCI_REQ_USE_DMA (1<<1) /* Use DMA for this req. */
@@ -261,6 +262,7 @@ struct sdhci_host {
struct mxc_mmc_platform_data *plat_data;
struct timer_list timer; /* Timer for timeouts */
+ struct timer_list cd_timer; /* Timer for cd */
};
struct sdhci_chip {