* published by the Free Software Foundation.
*/
-/* Why is a timer used to detect insert events?
+/* Why don't we use the SD controllers' carddetect feature?
*
* From the AU1100 MMC application guide:
* If the Au1100-based design is intended to support both MultiMediaCards
* In doing so, a MMC card never enters SPI-mode communications,
* but now the SecureDigital card-detect feature of CD/DAT3 is ineffective
* (the low to high transition will not occur).
- *
- * So we use the timer to check the status manually.
*/
#include <linux/module.h>
int irq;
- struct timer_list timer;
struct tasklet_struct finish_task;
struct tasklet_struct data_task;
struct au1xmmc_platform_data *platdata;
host->platdata->set_power(host->mmc, state);
}
-static int au1xmmc_card_inserted(struct au1xmmc_host *host)
+static int au1xmmc_card_inserted(struct mmc_host *mmc)
{
- int ret;
+ struct au1xmmc_host *host = mmc_priv(mmc);
if (host->platdata && host->platdata->card_inserted)
- ret = host->platdata->card_inserted(host->mmc);
- else
- ret = 1; /* assume there is a card */
+ return !!host->platdata->card_inserted(host->mmc);
- return ret;
+ return -ENOSYS;
}
static int au1xmmc_card_readonly(struct mmc_host *mmc)
{
struct au1xmmc_host *host = mmc_priv(mmc);
- int ret;
if (host->platdata && host->platdata->card_readonly)
- ret = host->platdata->card_readonly(mmc);
- else
- ret = 0; /* assume card is read-write */
+ return !!host->platdata->card_readonly(mmc);
- return ret;
+ return -ENOSYS;
}
static void au1xmmc_finish_request(struct au1xmmc_host *host)
host->mrq = mrq;
host->status = HOST_S_CMD;
+ /* fail request immediately if no card is present */
+ if (0 == au1xmmc_card_inserted(mmc)) {
+ mrq->cmd->error = -ENOMEDIUM;
+ au1xmmc_finish_request(host);
+ return;
+ }
+
if (mrq->data) {
FLUSH_FIFO(host);
ret = au1xmmc_prepare_data(host, mrq->data);
.request = au1xmmc_request,
.set_ios = au1xmmc_set_ios,
.get_ro = au1xmmc_card_readonly,
+ .get_cd = au1xmmc_card_inserted,
.enable_sdio_irq = au1xmmc_enable_sdio_irq,
};
-static void au1xmmc_poll_event(unsigned long arg)
-{
- struct au1xmmc_host *host = (struct au1xmmc_host *)arg;
- int card = au1xmmc_card_inserted(host);
- int controller = (host->flags & HOST_F_ACTIVE) ? 1 : 0;
-
- if (card != controller) {
- host->flags &= ~HOST_F_ACTIVE;
- if (card)
- host->flags |= HOST_F_ACTIVE;
- mmc_detect_change(host->mmc, 0);
- }
-
-#ifdef DEBUG
- if (host->mrq != NULL) {
- u32 status = au_readl(HOST_STATUS(host));
- DBG("PENDING - %8.8x\n", host->pdev->id, status);
- }
-#endif
- mod_timer(&host->timer, jiffies + AU1XMMC_DETECT_TIMEOUT);
-}
-
-static void au1xmmc_init_cd_poll_timer(struct au1xmmc_host *host)
-{
- init_timer(&host->timer);
- host->timer.function = au1xmmc_poll_event;
- host->timer.data = (unsigned long)host;
- host->timer.expires = jiffies + AU1XMMC_DETECT_TIMEOUT;
-}
-
static int __devinit au1xmmc_probe(struct platform_device *pdev)
{
struct mmc_host *mmc;
if (host->platdata && host->platdata->cd_setup) {
ret = host->platdata->cd_setup(mmc, 1);
if (ret) {
- dev_err(&pdev->dev, "board CD setup failed\n");
- goto out4;
+ dev_warn(&pdev->dev, "board CD setup failed\n");
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
}
- } else {
- /* poll the board-specific is-card-in-socket-? method */
- au1xmmc_init_cd_poll_timer(host);
- }
+ } else
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
tasklet_init(&host->data_task, au1xmmc_tasklet_data,
(unsigned long)host);
goto out6;
}
- platform_set_drvdata(pdev, mmc);
-
- /* start the carddetect poll timer if necessary */
- if (!(host->platdata && host->platdata->cd_setup))
- add_timer(&host->timer);
+ platform_set_drvdata(pdev, host);
printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X"
" (mode=%s)\n", pdev->id, host->iobase,
tasklet_kill(&host->data_task);
tasklet_kill(&host->finish_task);
- if (host->platdata && host->platdata->cd_setup)
+ if (host->platdata && host->platdata->cd_setup &&
+ !(mmc->caps & MMC_CAP_NEEDS_POLL))
host->platdata->cd_setup(mmc, 0);
-out4:
+
free_irq(host->irq, host);
out3:
iounmap((void *)host->iobase);
static int __devexit au1xmmc_remove(struct platform_device *pdev)
{
- struct mmc_host *mmc = platform_get_drvdata(pdev);
- struct au1xmmc_host *host;
-
- if (mmc) {
- host = mmc_priv(mmc);
+ struct au1xmmc_host *host = platform_get_drvdata(pdev);
- mmc_remove_host(mmc);
+ if (host) {
+ mmc_remove_host(host->mmc);
#ifdef CONFIG_LEDS_CLASS
if (host->platdata && host->platdata->led)
led_classdev_unregister(host->platdata->led);
#endif
- if (host->platdata && host->platdata->cd_setup)
- host->platdata->cd_setup(mmc, 0);
- else
- del_timer_sync(&host->timer);
+ if (host->platdata && host->platdata->cd_setup &&
+ !(host->mmc->caps & MMC_CAP_NEEDS_POLL))
+ host->platdata->cd_setup(host->mmc, 0);
au_writel(0, HOST_ENABLE(host));
au_writel(0, HOST_CONFIG(host));
release_resource(host->ioarea);
kfree(host->ioarea);
- mmc_free_host(mmc);
+ mmc_free_host(host->mmc);
+ platform_set_drvdata(pdev, NULL);
}
return 0;
}
+#ifdef CONFIG_PM
+static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct au1xmmc_host *host = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = mmc_suspend_host(host->mmc, state);
+ if (ret)
+ return ret;
+
+ au_writel(0, HOST_CONFIG2(host));
+ au_writel(0, HOST_CONFIG(host));
+ au_writel(0xffffffff, HOST_STATUS(host));
+ au_writel(0, HOST_ENABLE(host));
+ au_sync();
+
+ return 0;
+}
+
+static int au1xmmc_resume(struct platform_device *pdev)
+{
+ struct au1xmmc_host *host = platform_get_drvdata(pdev);
+
+ au1xmmc_reset_controller(host);
+
+ return mmc_resume_host(host->mmc);
+}
+#else
+#define au1xmmc_suspend NULL
+#define au1xmmc_resume NULL
+#endif
+
static struct platform_driver au1xmmc_driver = {
.probe = au1xmmc_probe,
.remove = au1xmmc_remove,
- .suspend = NULL,
- .resume = NULL,
+ .suspend = au1xmmc_suspend,
+ .resume = au1xmmc_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,