const: constify remaining dev_pm_ops
[safe/jmp/linux-2.6] / drivers / mmc / host / pxamci.c
index d39f597..0d783f3 100644 (file)
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/mmc/host.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
 
-#include <asm/dma.h>
-#include <asm/io.h>
 #include <asm/sizes.h>
 
-#include <asm/arch/pxa-regs.h>
-#include <asm/arch/mmc.h>
+#include <mach/hardware.h>
+#include <mach/dma.h>
+#include <mach/mmc.h>
 
 #include "pxamci.h"
 
@@ -41,6 +43,9 @@
 #define NR_SG  1
 #define CLKRT_OFF      (~0)
 
+#define mmc_has_26MHz()                (cpu_is_pxa300() || cpu_is_pxa310() \
+                               || cpu_is_pxa935())
+
 struct pxamci_host {
        struct mmc_host         *mmc;
        spinlock_t              lock;
@@ -67,8 +72,50 @@ struct pxamci_host {
        unsigned int            dma_dir;
        unsigned int            dma_drcmrrx;
        unsigned int            dma_drcmrtx;
+
+       struct regulator        *vcc;
 };
 
+static inline void pxamci_init_ocr(struct pxamci_host *host)
+{
+#ifdef CONFIG_REGULATOR
+       host->vcc = regulator_get(mmc_dev(host->mmc), "vmmc");
+
+       if (IS_ERR(host->vcc))
+               host->vcc = NULL;
+       else {
+               host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc);
+               if (host->pdata && host->pdata->ocr_mask)
+                       dev_warn(mmc_dev(host->mmc),
+                               "ocr_mask/setpower will not be used\n");
+       }
+#endif
+       if (host->vcc == NULL) {
+               /* fall-back to platform data */
+               host->mmc->ocr_avail = host->pdata ?
+                       host->pdata->ocr_mask :
+                       MMC_VDD_32_33 | MMC_VDD_33_34;
+       }
+}
+
+static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd)
+{
+       int on;
+
+#ifdef CONFIG_REGULATOR
+       if (host->vcc)
+               mmc_regulator_set_ocr(host->vcc, vdd);
+#endif
+       if (!host->vcc && host->pdata &&
+           gpio_is_valid(host->pdata->gpio_power)) {
+               on = ((1 << vdd) & host->pdata->ocr_mask);
+               gpio_set_value(host->pdata->gpio_power,
+                              !!on ^ host->pdata->gpio_power_invert);
+       }
+       if (!host->vcc && host->pdata && host->pdata->setpower)
+               host->pdata->setpower(mmc_dev(host->mmc), vdd);
+}
+
 static void pxamci_stop_clock(struct pxamci_host *host)
 {
        if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
@@ -133,12 +180,12 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
 
        if (data->flags & MMC_DATA_READ) {
                host->dma_dir = DMA_FROM_DEVICE;
-               dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG;
+               dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC;
                DRCMR(host->dma_drcmrtx) = 0;
                DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD;
        } else {
                host->dma_dir = DMA_TO_DEVICE;
-               dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC;
+               dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG;
                DRCMR(host->dma_drcmrrx) = 0;
                DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD;
        }
@@ -177,9 +224,17 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
        if (dalgn)
                DALGN |= (1 << host->dma);
        else
-               DALGN &= (1 << host->dma);
+               DALGN &= ~(1 << host->dma);
        DDADR(host->dma) = host->sg_dma;
-       DCSR(host->dma) = DCSR_RUN;
+
+       /*
+        * workaround for erratum #91:
+        * only start DMA now if we are doing a read,
+        * otherwise we wait until CMD/RESP has finished
+        * before starting DMA.
+        */
+       if (!cpu_is_pxa27x() || data->flags & MMC_DATA_READ)
+               DCSR(host->dma) = DCSR_RUN;
 }
 
 static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat)
@@ -250,23 +305,28 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
        if (stat & STAT_TIME_OUT_RESPONSE) {
                cmd->error = -ETIMEDOUT;
        } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
-#ifdef CONFIG_PXA27x
                /*
                 * workaround for erratum #42:
                 * Intel PXA27x Family Processor Specification Update Rev 001
                 * A bogus CRC error can appear if the msb of a 136 bit
                 * response is a one.
                 */
-               if (cmd->flags & MMC_RSP_136 && cmd->resp[0] & 0x80000000) {
+               if (cpu_is_pxa27x() &&
+                   (cmd->flags & MMC_RSP_136 && cmd->resp[0] & 0x80000000))
                        pr_debug("ignoring CRC from command %d - *risky*\n", cmd->opcode);
-               } else
-#endif
-               cmd->error = -EILSEQ;
+               else
+                       cmd->error = -EILSEQ;
        }
 
        pxamci_disable_irq(host, END_CMD_RES);
        if (host->data && !cmd->error) {
                pxamci_enable_irq(host, DATA_TRAN_DONE);
+               /*
+                * workaround for erratum #91, if doing write
+                * enable DMA late
+                */
+               if (cpu_is_pxa27x() && host->data->flags & MMC_DATA_WRITE)
+                       DCSR(host->dma) = DCSR_RUN;
        } else {
                pxamci_finish_request(host, host->mrq);
        }
@@ -282,7 +342,7 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
                return 0;
 
        DCSR(host->dma) = 0;
-       dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
+       dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
                     host->dma_dir);
 
        if (stat & STAT_READ_TIME_OUT)
@@ -373,6 +433,12 @@ static int pxamci_get_ro(struct mmc_host *mmc)
 {
        struct pxamci_host *host = mmc_priv(mmc);
 
+       if (host->pdata && gpio_is_valid(host->pdata->gpio_card_ro)) {
+               if (host->pdata->gpio_card_ro_invert)
+                       return !gpio_get_value(host->pdata->gpio_card_ro);
+               else
+                       return gpio_get_value(host->pdata->gpio_card_ro);
+       }
        if (host->pdata && host->pdata->get_ro)
                return !!host->pdata->get_ro(mmc_dev(mmc));
        /*
@@ -394,7 +460,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                        clk_enable(host->clk);
 
                if (ios->clock == 26000000) {
-                       /* to support 26MHz on pxa300/pxa310 */
+                       /* to support 26MHz */
                        host->clkrt = 7;
                } else {
                        /* to handle (19.5MHz, 26MHz) */
@@ -425,8 +491,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        if (host->power_mode != ios->power_mode) {
                host->power_mode = ios->power_mode;
 
-               if (host->pdata && host->pdata->setpower)
-                       host->pdata->setpower(mmc_dev(mmc), ios->vdd);
+               pxamci_set_power(host, ios->vdd);
 
                if (ios->power_mode == MMC_POWER_ON)
                        host->cmdat |= CMDAT_INIT;
@@ -487,7 +552,7 @@ static int pxamci_probe(struct platform_device *pdev)
        struct mmc_host *mmc;
        struct pxamci_host *host = NULL;
        struct resource *r, *dmarx, *dmatx;
-       int ret, irq;
+       int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1;
 
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        irq = platform_get_irq(pdev, 0);
@@ -520,7 +585,7 @@ static int pxamci_probe(struct platform_device *pdev)
        /*
         * Block length register is only 10 bits before PXA27x.
         */
-       mmc->max_blk_size = (cpu_is_pxa21x() || cpu_is_pxa25x()) ? 1023 : 2048;
+       mmc->max_blk_size = cpu_is_pxa25x() ? 1023 : 2048;
 
        /*
         * Block count register is 16 bits.
@@ -533,7 +598,7 @@ static int pxamci_probe(struct platform_device *pdev)
        host->pdata = pdev->dev.platform_data;
        host->clkrt = CLKRT_OFF;
 
-       host->clk = clk_get(&pdev->dev, "MMCCLK");
+       host->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(host->clk)) {
                ret = PTR_ERR(host->clk);
                host->clk = NULL;
@@ -546,18 +611,16 @@ static int pxamci_probe(struct platform_device *pdev)
         * Calculate minimum clock rate, rounding up.
         */
        mmc->f_min = (host->clkrate + 63) / 64;
-       mmc->f_max = (cpu_is_pxa300() || cpu_is_pxa310()) ? 26000000
-                                                         : host->clkrate;
+       mmc->f_max = (mmc_has_26MHz()) ? 26000000 : host->clkrate;
+
+       pxamci_init_ocr(host);
 
-       mmc->ocr_avail = host->pdata ?
-                        host->pdata->ocr_mask :
-                        MMC_VDD_32_33|MMC_VDD_33_34;
        mmc->caps = 0;
        host->cmdat = 0;
-       if (!cpu_is_pxa21x() && !cpu_is_pxa25x()) {
+       if (!cpu_is_pxa25x()) {
                mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
                host->cmdat |= CMDAT_SDIO_INT_EN;
-               if (cpu_is_pxa300() || cpu_is_pxa310())
+               if (mmc_has_26MHz())
                        mmc->caps |= MMC_CAP_MMC_HIGHSPEED |
                                     MMC_CAP_SD_HIGHSPEED;
        }
@@ -615,13 +678,63 @@ static int pxamci_probe(struct platform_device *pdev)
        }
        host->dma_drcmrtx = dmatx->start;
 
+       if (host->pdata) {
+               gpio_cd = host->pdata->gpio_card_detect;
+               gpio_ro = host->pdata->gpio_card_ro;
+               gpio_power = host->pdata->gpio_power;
+       }
+       if (gpio_is_valid(gpio_power)) {
+               ret = gpio_request(gpio_power, "mmc card power");
+               if (ret) {
+                       dev_err(&pdev->dev, "Failed requesting gpio_power %d\n", gpio_power);
+                       goto out;
+               }
+               gpio_direction_output(gpio_power,
+                                     host->pdata->gpio_power_invert);
+       }
+       if (gpio_is_valid(gpio_ro)) {
+               ret = gpio_request(gpio_ro, "mmc card read only");
+               if (ret) {
+                       dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_ro);
+                       goto err_gpio_ro;
+               }
+               gpio_direction_input(gpio_ro);
+       }
+       if (gpio_is_valid(gpio_cd)) {
+               ret = gpio_request(gpio_cd, "mmc card detect");
+               if (ret) {
+                       dev_err(&pdev->dev, "Failed requesting gpio_cd %d\n", gpio_cd);
+                       goto err_gpio_cd;
+               }
+               gpio_direction_input(gpio_cd);
+
+               ret = request_irq(gpio_to_irq(gpio_cd), pxamci_detect_irq,
+                                 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                                 "mmc card detect", mmc);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to request card detect IRQ\n");
+                       goto err_request_irq;
+               }
+       }
+
        if (host->pdata && host->pdata->init)
                host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc);
 
+       if (gpio_is_valid(gpio_power) && host->pdata->setpower)
+               dev_warn(&pdev->dev, "gpio_power and setpower() both defined\n");
+       if (gpio_is_valid(gpio_ro) && host->pdata->get_ro)
+               dev_warn(&pdev->dev, "gpio_ro and get_ro() both defined\n");
+
        mmc_add_host(mmc);
 
        return 0;
 
+err_request_irq:
+       gpio_free(gpio_cd);
+err_gpio_cd:
+       gpio_free(gpio_ro);
+err_gpio_ro:
+       gpio_free(gpio_power);
  out:
        if (host) {
                if (host->dma >= 0)
@@ -642,17 +755,34 @@ static int pxamci_probe(struct platform_device *pdev)
 static int pxamci_remove(struct platform_device *pdev)
 {
        struct mmc_host *mmc = platform_get_drvdata(pdev);
+       int gpio_cd = -1, gpio_ro = -1, gpio_power = -1;
 
        platform_set_drvdata(pdev, NULL);
 
        if (mmc) {
                struct pxamci_host *host = mmc_priv(mmc);
 
+               mmc_remove_host(mmc);
+
+               if (host->pdata) {
+                       gpio_cd = host->pdata->gpio_card_detect;
+                       gpio_ro = host->pdata->gpio_card_ro;
+                       gpio_power = host->pdata->gpio_power;
+               }
+               if (gpio_is_valid(gpio_cd)) {
+                       free_irq(gpio_to_irq(gpio_cd), mmc);
+                       gpio_free(gpio_cd);
+               }
+               if (gpio_is_valid(gpio_ro))
+                       gpio_free(gpio_ro);
+               if (gpio_is_valid(gpio_power))
+                       gpio_free(gpio_power);
+               if (host->vcc)
+                       regulator_put(host->vcc);
+
                if (host->pdata && host->pdata->exit)
                        host->pdata->exit(&pdev->dev, mmc);
 
-               mmc_remove_host(mmc);
-
                pxamci_stop_clock(host);
                writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD|
                       END_CMD_RES|PRG_DONE|DATA_TRAN_DONE,
@@ -676,20 +806,20 @@ static int pxamci_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM
-static int pxamci_suspend(struct platform_device *dev, pm_message_t state)
+static int pxamci_suspend(struct device *dev)
 {
-       struct mmc_host *mmc = platform_get_drvdata(dev);
+       struct mmc_host *mmc = dev_get_drvdata(dev);
        int ret = 0;
 
        if (mmc)
-               ret = mmc_suspend_host(mmc, state);
+               ret = mmc_suspend_host(mmc, PMSG_SUSPEND);
 
        return ret;
 }
 
-static int pxamci_resume(struct platform_device *dev)
+static int pxamci_resume(struct device *dev)
 {
-       struct mmc_host *mmc = platform_get_drvdata(dev);
+       struct mmc_host *mmc = dev_get_drvdata(dev);
        int ret = 0;
 
        if (mmc)
@@ -697,19 +827,22 @@ static int pxamci_resume(struct platform_device *dev)
 
        return ret;
 }
-#else
-#define pxamci_suspend NULL
-#define pxamci_resume  NULL
+
+static const struct dev_pm_ops pxamci_pm_ops = {
+       .suspend        = pxamci_suspend,
+       .resume         = pxamci_resume,
+};
 #endif
 
 static struct platform_driver pxamci_driver = {
        .probe          = pxamci_probe,
        .remove         = pxamci_remove,
-       .suspend        = pxamci_suspend,
-       .resume         = pxamci_resume,
        .driver         = {
                .name   = DRIVER_NAME,
                .owner  = THIS_MODULE,
+#ifdef CONFIG_PM
+               .pm     = &pxamci_pm_ops,
+#endif
        },
 };