]> err.no Git - linux-2.6/blobdiff - drivers/mmc/host/au1xmmc.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/sam/kbuild-fixes
[linux-2.6] / drivers / mmc / host / au1xmmc.c
index fcbaf40e35535f41bf873f60a4e420c210df7fbb..d3f55615c0990e46176a0afd49ed0c55977833ef 100644 (file)
@@ -21,7 +21,7 @@
  * 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
@@ -30,8 +30,6 @@
  * 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>
 
 /* Hardware definitions */
 #define AU1XMMC_DESCRIPTOR_COUNT 1
-#define AU1XMMC_DESCRIPTOR_SIZE  2048
+
+/* max DMA seg size: 64KB on Au1100, 4MB on Au1200 */
+#ifdef CONFIG_SOC_AU1100
+#define AU1XMMC_DESCRIPTOR_SIZE 0x0000ffff
+#else  /* Au1200 */
+#define AU1XMMC_DESCRIPTOR_SIZE 0x003fffff
+#endif
 
 #define AU1XMMC_OCR (MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 | \
                     MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 | \
@@ -111,7 +115,6 @@ struct au1xmmc_host {
 
        int irq;
 
-       struct timer_list timer;
        struct tasklet_struct finish_task;
        struct tasklet_struct data_task;
        struct au1xmmc_platform_data *platdata;
@@ -198,29 +201,24 @@ static void au1xmmc_set_power(struct au1xmmc_host *host, int state)
                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)
@@ -697,6 +695,13 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
        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);
@@ -928,39 +933,10 @@ static const struct mmc_host_ops au1xmmc_ops = {
        .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;
@@ -1035,13 +1011,11 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
        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);
@@ -1075,11 +1049,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
                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,
@@ -1105,9 +1075,10 @@ out5:
        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);
@@ -1122,23 +1093,19 @@ out0:
 
 static int __devexit au1xmmc_remove(struct platform_device *pdev)
 {
-       struct mmc_host *mmc = platform_get_drvdata(pdev);
-       struct au1xmmc_host *host;
+       struct au1xmmc_host *host = platform_get_drvdata(pdev);
 
-       if (mmc) {
-               host  = mmc_priv(mmc);
-
-               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));
@@ -1158,16 +1125,49 @@ static int __devexit au1xmmc_remove(struct platform_device *pdev)
                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,