drivers/mmc/host/sdhci.h needs scatterlist.h
[safe/jmp/linux-2.6] / drivers / mmc / host / s3cmci.c
index 684a10c..be550c2 100644 (file)
@@ -353,7 +353,8 @@ static void pio_tasklet(unsigned long data)
                            (host->pio_active == XFER_READ) ? "read" : "write",
                            host->pio_count, host->pio_words);
 
-                       host->mrq->data->error = -EINVAL;
+                       if (host->mrq->data)
+                               host->mrq->data->error = -EINVAL;
                }
 
                finalize_request(host);
@@ -449,6 +450,7 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id)
        }
 
        if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
+               dbg(host, dbg_err, "CMDSTAT: error CMDTIMEOUT\n");
                cmd->error = -ETIMEDOUT;
                host->status = "error: command timeout";
                goto fail_transfer;
@@ -504,12 +506,14 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id)
        /* Check for FIFO failure */
        if (host->is2440) {
                if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) {
+                       dbg(host, dbg_err, "FIFO failure\n");
                        host->mrq->data->error = -EILSEQ;
                        host->status = "error: 2440 fifo failure";
                        goto fail_transfer;
                }
        } else {
                if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
+                       dbg(host, dbg_err, "FIFO failure\n");
                        cmd->data->error = -EILSEQ;
                        host->status = "error:  fifo failure";
                        goto fail_transfer;
@@ -517,18 +521,21 @@ static irqreturn_t s3cmci_irq(int irq, void *dev_id)
        }
 
        if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
+               dbg(host, dbg_err, "bad data crc (outgoing)\n");
                cmd->data->error = -EILSEQ;
                host->status = "error: bad data crc (outgoing)";
                goto fail_transfer;
        }
 
        if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {
+               dbg(host, dbg_err, "bad data crc (incoming)\n");
                cmd->data->error = -EILSEQ;
                host->status = "error: bad data crc (incoming)";
                goto fail_transfer;
        }
 
        if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
+               dbg(host, dbg_err, "data timeout\n");
                cmd->data->error = -ETIMEDOUT;
                host->status = "error: data timeout";
                goto fail_transfer;
@@ -583,7 +590,7 @@ static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
 
        dbg(host, dbg_irq, "card detect\n");
 
-       mmc_detect_change(host->mmc, 500);
+       mmc_detect_change(host->mmc, msecs_to_jiffies(500));
 
        return IRQ_HANDLED;
 }
@@ -800,6 +807,17 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
                return 0;
        }
 
+       if ((data->blksz & 3) != 0) {
+               /* We cannot deal with unaligned blocks with more than
+                * one block being transfered. */
+
+               if (data->blocks > 1)
+                       return -EINVAL;
+
+               /* No support yet for non-word block transfers. */
+               return -EINVAL;
+       }
+
        while (readl(host->base + S3C2410_SDIDSTA) &
               (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
 
@@ -955,8 +973,9 @@ static void s3cmci_send_request(struct mmc_host *mmc)
                host->dcnt++;
 
                if (res) {
-                       cmd->error = -EINVAL;
-                       cmd->data->error = -EINVAL;
+                       dbg(host, dbg_err, "setup data error %d\n", res);
+                       cmd->error = res;
+                       cmd->data->error = res;
 
                        mmc_request_done(mmc, mrq);
                        return;
@@ -968,6 +987,7 @@ static void s3cmci_send_request(struct mmc_host *mmc)
                        res = s3cmci_prepare_pio(host, cmd->data);
 
                if (res) {
+                       dbg(host, dbg_err, "data prepare error %d\n", res);
                        cmd->error = res;
                        cmd->data->error = res;
 
@@ -983,6 +1003,18 @@ static void s3cmci_send_request(struct mmc_host *mmc)
        enable_irq(host->irq);
 }
 
+static int s3cmci_card_present(struct s3cmci_host *host)
+{
+       struct s3c24xx_mci_pdata *pdata = host->pdata;
+       int ret;
+
+       if (pdata->gpio_detect == 0)
+               return -ENOSYS;
+
+       ret = s3c2410_gpio_getpin(pdata->gpio_detect) ? 0 : 1;
+       return ret ^ pdata->detect_invert;
+}
+
 static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
        struct s3cmci_host *host = mmc_priv(mmc);
@@ -991,7 +1023,12 @@ static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
        host->cmd_is_stop = 0;
        host->mrq = mrq;
 
-       s3cmci_send_request(mmc);
+       if (s3cmci_card_present(host) == 0) {
+               dbg(host, dbg_err, "%s: no medium present\n", __func__);
+               host->mrq->cmd->error = -ENOMEDIUM;
+               mmc_request_done(mmc, mrq);
+       } else
+               s3cmci_send_request(mmc);
 }
 
 static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -1083,11 +1120,18 @@ static void s3cmci_reset(struct s3cmci_host *host)
 static int s3cmci_get_ro(struct mmc_host *mmc)
 {
        struct s3cmci_host *host = mmc_priv(mmc);
+       struct s3c24xx_mci_pdata *pdata = host->pdata;
+       int ret;
 
-       if (host->pdata->gpio_wprotect == 0)
+       if (pdata->gpio_wprotect == 0)
                return 0;
 
-       return s3c2410_gpio_getpin(host->pdata->gpio_wprotect);
+       ret = s3c2410_gpio_getpin(pdata->gpio_wprotect);
+
+       if (pdata->wprotect_invert)
+               ret = !ret;
+
+       return ret;
 }
 
 static struct mmc_host_ops s3cmci_ops = {
@@ -1142,8 +1186,6 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
        host->pio_active        = XFER_NONE;
 
        host->dma               = S3CMCI_DMA;
-       host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
-       s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ);
 
        host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!host->mem) {
@@ -1189,21 +1231,27 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
 
        disable_irq(host->irq);
 
-       s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ);
-       set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
+       host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
 
-       if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
-               dev_err(&pdev->dev,
-                       "failed to request card detect interrupt.\n");
-               ret = -ENOENT;
-               goto probe_free_irq;
+       if (host->irq_cd >= 0) {
+               if (request_irq(host->irq_cd, s3cmci_irq_cd,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                               DRIVER_NAME, host)) {
+                       dev_err(&pdev->dev, "can't get card detect irq.\n");
+                       ret = -ENOENT;
+                       goto probe_free_irq;
+               }
+       } else {
+               dev_warn(&pdev->dev, "host detect has no irq available\n");
+               s3c2410_gpio_cfgpin(host->pdata->gpio_detect,
+                                   S3C2410_GPIO_INPUT);
        }
 
        if (host->pdata->gpio_wprotect)
                s3c2410_gpio_cfgpin(host->pdata->gpio_wprotect,
                                    S3C2410_GPIO_INPUT);
 
-       if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
+       if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL) < 0) {
                dev_err(&pdev->dev, "unable to get DMA channel.\n");
                ret = -EBUSY;
                goto probe_free_irq_cd;
@@ -1265,7 +1313,8 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
        clk_put(host->clk);
 
  probe_free_irq_cd:
-       free_irq(host->irq_cd, host);
+       if (host->irq_cd >= 0)
+               free_irq(host->irq_cd, host);
 
  probe_free_irq:
        free_irq(host->irq, host);
@@ -1282,20 +1331,30 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
        return ret;
 }
 
+static void s3cmci_shutdown(struct platform_device *pdev)
+{
+       struct mmc_host *mmc = platform_get_drvdata(pdev);
+       struct s3cmci_host *host = mmc_priv(mmc);
+
+       if (host->irq_cd >= 0)
+               free_irq(host->irq_cd, host);
+
+       mmc_remove_host(mmc);
+       clk_disable(host->clk);
+}
+
 static int __devexit s3cmci_remove(struct platform_device *pdev)
 {
        struct mmc_host         *mmc  = platform_get_drvdata(pdev);
        struct s3cmci_host      *host = mmc_priv(mmc);
 
-       mmc_remove_host(mmc);
+       s3cmci_shutdown(pdev);
 
-       clk_disable(host->clk);
        clk_put(host->clk);
 
        tasklet_disable(&host->pio_tasklet);
        s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client);
 
-       free_irq(host->irq_cd, host);
        free_irq(host->irq, host);
 
        iounmap(host->base);
@@ -1305,17 +1364,17 @@ static int __devexit s3cmci_remove(struct platform_device *pdev)
        return 0;
 }
 
-static int __devinit s3cmci_probe_2410(struct platform_device *dev)
+static int __devinit s3cmci_2410_probe(struct platform_device *dev)
 {
        return s3cmci_probe(dev, 0);
 }
 
-static int __devinit s3cmci_probe_2412(struct platform_device *dev)
+static int __devinit s3cmci_2412_probe(struct platform_device *dev)
 {
        return s3cmci_probe(dev, 1);
 }
 
-static int __devinit s3cmci_probe_2440(struct platform_device *dev)
+static int __devinit s3cmci_2440_probe(struct platform_device *dev)
 {
        return s3cmci_probe(dev, 1);
 }
@@ -1342,29 +1401,32 @@ static int s3cmci_resume(struct platform_device *dev)
 #endif /* CONFIG_PM */
 
 
-static struct platform_driver s3cmci_driver_2410 = {
+static struct platform_driver s3cmci_2410_driver = {
        .driver.name    = "s3c2410-sdi",
        .driver.owner   = THIS_MODULE,
-       .probe          = s3cmci_probe_2410,
+       .probe          = s3cmci_2410_probe,
        .remove         = __devexit_p(s3cmci_remove),
+       .shutdown       = s3cmci_shutdown,
        .suspend        = s3cmci_suspend,
        .resume         = s3cmci_resume,
 };
 
-static struct platform_driver s3cmci_driver_2412 = {
+static struct platform_driver s3cmci_2412_driver = {
        .driver.name    = "s3c2412-sdi",
        .driver.owner   = THIS_MODULE,
-       .probe          = s3cmci_probe_2412,
+       .probe          = s3cmci_2412_probe,
        .remove         = __devexit_p(s3cmci_remove),
+       .shutdown       = s3cmci_shutdown,
        .suspend        = s3cmci_suspend,
        .resume         = s3cmci_resume,
 };
 
-static struct platform_driver s3cmci_driver_2440 = {
+static struct platform_driver s3cmci_2440_driver = {
        .driver.name    = "s3c2440-sdi",
        .driver.owner   = THIS_MODULE,
-       .probe          = s3cmci_probe_2440,
+       .probe          = s3cmci_2440_probe,
        .remove         = __devexit_p(s3cmci_remove),
+       .shutdown       = s3cmci_shutdown,
        .suspend        = s3cmci_suspend,
        .resume         = s3cmci_resume,
 };
@@ -1372,17 +1434,17 @@ static struct platform_driver s3cmci_driver_2440 = {
 
 static int __init s3cmci_init(void)
 {
-       platform_driver_register(&s3cmci_driver_2410);
-       platform_driver_register(&s3cmci_driver_2412);
-       platform_driver_register(&s3cmci_driver_2440);
+       platform_driver_register(&s3cmci_2410_driver);
+       platform_driver_register(&s3cmci_2412_driver);
+       platform_driver_register(&s3cmci_2440_driver);
        return 0;
 }
 
 static void __exit s3cmci_exit(void)
 {
-       platform_driver_unregister(&s3cmci_driver_2410);
-       platform_driver_unregister(&s3cmci_driver_2412);
-       platform_driver_unregister(&s3cmci_driver_2440);
+       platform_driver_unregister(&s3cmci_2410_driver);
+       platform_driver_unregister(&s3cmci_2412_driver);
+       platform_driver_unregister(&s3cmci_2440_driver);
 }
 
 module_init(s3cmci_init);
@@ -1391,3 +1453,6 @@ module_exit(s3cmci_exit);
 MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>");
+MODULE_ALIAS("platform:s3c2410-sdi");
+MODULE_ALIAS("platform:s3c2412-sdi");
+MODULE_ALIAS("platform:s3c2440-sdi");