Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / mmc / pxamci.c
index 23a5091..b49368f 100644 (file)
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
-#include <linux/blkdev.h>
 #include <linux/dma-mapping.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/protocol.h>
 
 #include <asm/dma.h>
 #include <asm/io.h>
-#include <asm/irq.h>
+#include <asm/scatterlist.h>
 #include <asm/sizes.h>
 
 #include <asm/arch/pxa-regs.h>
 
 #include "pxamci.h"
 
-#ifdef CONFIG_MMC_DEBUG
-#define DBG(x...)      printk(KERN_DEBUG x)
-#else
-#define DBG(x...)      do { } while (0)
-#endif
+#define DRIVER_NAME    "pxa2xx-mci"
+
+#define NR_SG  1
 
 struct pxamci_host {
        struct mmc_host         *mmc;
        spinlock_t              lock;
        struct resource         *res;
-       void                    *base;
+       void __iomem            *base;
        int                     irq;
        int                     dma;
        unsigned int            clkrt;
@@ -63,22 +60,11 @@ struct pxamci_host {
 
        dma_addr_t              sg_dma;
        struct pxa_dma_desc     *sg_cpu;
+       unsigned int            dma_len;
 
-       dma_addr_t              dma_buf;
-       unsigned int            dma_size;
        unsigned int            dma_dir;
 };
 
-/*
- * The base MMC clock rate
- */
-#define CLOCKRATE      20000000
-
-static inline unsigned int ns_to_clocks(unsigned int ns)
-{
-       return (ns * (CLOCKRATE / 1000000) + 999) / 1000;
-}
-
 static void pxamci_stop_clock(struct pxamci_host *host)
 {
        if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
@@ -122,8 +108,8 @@ static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask)
 static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
 {
        unsigned int nob = data->blocks;
-       unsigned int timeout, size;
-       dma_addr_t dma;
+       unsigned long long clks;
+       unsigned int timeout;
        u32 dcmd;
        int i;
 
@@ -133,9 +119,11 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
                nob = 0xffff;
 
        writel(nob, host->base + MMC_NOB);
-       writel(1 << data->blksz_bits, host->base + MMC_BLKLEN);
+       writel(data->blksz, host->base + MMC_BLKLEN);
 
-       timeout = ns_to_clocks(data->timeout_ns) + data->timeout_clks;
+       clks = (unsigned long long)data->timeout_ns * CLOCKRATE;
+       do_div(clks, 1000000000UL);
+       timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt);
        writel((timeout + 255) / 256, host->base + MMC_RDTO);
 
        if (data->flags & MMC_DATA_READ) {
@@ -152,35 +140,22 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
 
        dcmd |= DCMD_BURST32 | DCMD_WIDTH1;
 
-       host->dma_size = data->blocks << data->blksz_bits;
-       host->dma_buf = dma_map_single(mmc_dev(host->mmc), data->req->buffer,
-                                      host->dma_size, host->dma_dir);
-
-       for (i = 0, size = host->dma_size, dma = host->dma_buf; size; i++) {
-               u32 len = size;
-
-               if (len > DCMD_LENGTH)
-                       len = 0x1000;
+       host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+                                  host->dma_dir);
 
+       for (i = 0; i < host->dma_len; i++) {
                if (data->flags & MMC_DATA_READ) {
                        host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
-                       host->sg_cpu[i].dtadr = dma;
+                       host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
                } else {
-                       host->sg_cpu[i].dsadr = dma;
+                       host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]);
                        host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO;
                }
-               host->sg_cpu[i].dcmd = dcmd | len;
-
-               dma += len;
-               size -= len;
-
-               if (size) {
-                       host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
-                                                sizeof(struct pxa_dma_desc);
-               } else {
-                       host->sg_cpu[i].ddadr = DDADR_STOP;
-               }
+               host->sg_cpu[i].dcmd = dcmd | sg_dma_len(&data->sg[i]);
+               host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
+                                       sizeof(struct pxa_dma_desc);
        }
+       host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP;
        wmb();
 
        DDADR(host->dma) = host->sg_dma;
@@ -195,14 +170,15 @@ static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd,
        if (cmd->flags & MMC_RSP_BUSY)
                cmdat |= CMDAT_BUSY;
 
-       switch (cmd->flags & (MMC_RSP_MASK | MMC_RSP_CRC)) {
-       case MMC_RSP_SHORT | MMC_RSP_CRC:
+#define RSP_TYPE(x)    ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
+       switch (RSP_TYPE(mmc_resp_type(cmd))) {
+       case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6 */
                cmdat |= CMDAT_RESP_SHORT;
                break;
-       case MMC_RSP_SHORT:
+       case RSP_TYPE(MMC_RSP_R3):
                cmdat |= CMDAT_RESP_R3;
                break;
-       case MMC_RSP_LONG | MMC_RSP_CRC:
+       case RSP_TYPE(MMC_RSP_R2):
                cmdat |= CMDAT_RESP_R2;
                break;
        default:
@@ -222,7 +198,6 @@ static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd,
 
 static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq)
 {
-       DBG("PXAMCI: request done\n");
        host->mrq = NULL;
        host->cmd = NULL;
        host->data = NULL;
@@ -255,7 +230,24 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
        if (stat & STAT_TIME_OUT_RESPONSE) {
                cmd->error = MMC_ERR_TIMEOUT;
        } 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
+                */
+               if (cmd->opcode == MMC_ALL_SEND_CID ||
+                   cmd->opcode == MMC_SEND_CSD ||
+                   cmd->opcode == MMC_SEND_CID) {
+                       /* a bogus CRC error can appear if the msb of
+                          the 15 byte response is a one */
+                       if ((cmd->resp[0] & 0x80000000) == 0)
+                               cmd->error = MMC_ERR_BADCRC;
+               } else {
+                       pr_debug("ignoring CRC from command %d - *risky*\n",cmd->opcode);
+               }
+#else
                cmd->error = MMC_ERR_BADCRC;
+#endif
        }
 
        pxamci_disable_irq(host, END_CMD_RES);
@@ -276,8 +268,8 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
                return 0;
 
        DCSR(host->dma) = 0;
-       dma_unmap_single(mmc_dev(host->mmc), host->dma_buf, host->dma_size,
-                        host->dma_dir);
+       dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
+                    host->dma_dir);
 
        if (stat & STAT_READ_TIME_OUT)
                data->error = MMC_ERR_TIMEOUT;
@@ -291,14 +283,14 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
         * data blocks as being in error.
         */
        if (data->error == MMC_ERR_NONE)
-               data->bytes_xfered = data->blocks << data->blksz_bits;
+               data->bytes_xfered = data->blocks * data->blksz;
        else
                data->bytes_xfered = 0;
 
        pxamci_disable_irq(host, DATA_TRAN_DONE);
 
        host->data = NULL;
-       if (host->mrq->stop && data->error == MMC_ERR_NONE) {
+       if (host->mrq->stop) {
                pxamci_stop_clock(host);
                pxamci_start_cmd(host, host->mrq->stop, 0);
        } else {
@@ -316,12 +308,10 @@ static irqreturn_t pxamci_irq(int irq, void *devid, struct pt_regs *regs)
 
        ireg = readl(host->base + MMC_I_REG);
 
-       DBG("PXAMCI: irq %08x\n", ireg);
-
        if (ireg) {
                unsigned stat = readl(host->base + MMC_STAT);
 
-               DBG("PXAMCI: stat %08x\n", stat);
+               pr_debug("PXAMCI: irq %08x stat %08x\n", ireg, stat);
 
                if (ireg & END_CMD_RES)
                        handled |= pxamci_cmd_done(host, stat);
@@ -361,28 +351,33 @@ static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq)
        pxamci_start_cmd(host, mrq->cmd, cmdat);
 }
 
-static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+static int pxamci_get_ro(struct mmc_host *mmc)
 {
        struct pxamci_host *host = mmc_priv(mmc);
 
-       DBG("pxamci_set_ios: clock %u power %u vdd %u.%02u\n",
-           ios->clock, ios->power_mode, ios->vdd / 100,
-           ios->vdd % 100);
+       if (host->pdata && host->pdata->get_ro)
+               return host->pdata->get_ro(mmc->dev);
+       /* Host doesn't support read only detection so assume writeable */
+       return 0;
+}
+
+static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct pxamci_host *host = mmc_priv(mmc);
 
        if (ios->clock) {
                unsigned int clk = CLOCKRATE / ios->clock;
                if (CLOCKRATE / clk > ios->clock)
                        clk <<= 1;
                host->clkrt = fls(clk) - 1;
+               pxa_set_cken(CKEN12_MMC, 1);
 
                /*
                 * we write clkrt on the next command
                 */
-       } else if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
-               /*
-                * Ensure that the clock is off.
-                */
-               writel(STOP_CLOCK, host->base + MMC_STRPCL);
+       } else {
+               pxamci_stop_clock(host);
+               pxa_set_cken(CKEN12_MMC, 0);
        }
 
        if (host->power_mode != ios->power_mode) {
@@ -395,12 +390,13 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                        host->cmdat |= CMDAT_INIT;
        }
 
-       DBG("pxamci_set_ios: clkrt = %x cmdat = %x\n",
-           host->clkrt, host->cmdat);
+       pr_debug("PXAMCI: clkrt = %x cmdat = %x\n",
+                host->clkrt, host->cmdat);
 }
 
 static struct mmc_host_ops pxamci_ops = {
        .request        = pxamci_request,
+       .get_ro         = pxamci_get_ro,
        .set_ios        = pxamci_set_ios,
 };
 
@@ -412,13 +408,14 @@ static void pxamci_dma_irq(int dma, void *devid, struct pt_regs *regs)
 
 static irqreturn_t pxamci_detect_irq(int irq, void *devid, struct pt_regs *regs)
 {
-       mmc_detect_change(devid);
+       struct pxamci_host *host = mmc_priv(devid);
+
+       mmc_detect_change(devid, host->pdata->detect_delay);
        return IRQ_HANDLED;
 }
 
-static int pxamci_probe(struct device *dev)
+static int pxamci_probe(struct platform_device *pdev)
 {
-       struct platform_device *pdev = to_platform_device(dev);
        struct mmc_host *mmc;
        struct pxamci_host *host = NULL;
        struct resource *r;
@@ -426,22 +423,33 @@ static int pxamci_probe(struct device *dev)
 
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        irq = platform_get_irq(pdev, 0);
-       if (!r || irq == NO_IRQ)
+       if (!r || irq < 0)
                return -ENXIO;
 
-       r = request_mem_region(r->start, SZ_4K, "PXAMCI");
+       r = request_mem_region(r->start, SZ_4K, DRIVER_NAME);
        if (!r)
                return -EBUSY;
 
-       mmc = mmc_alloc_host(sizeof(struct pxamci_host), dev);
+       mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev);
        if (!mmc) {
                ret = -ENOMEM;
                goto out;
        }
 
        mmc->ops = &pxamci_ops;
-       mmc->f_min = 312500;
-       mmc->f_max = 20000000;
+       mmc->f_min = CLOCKRATE_MIN;
+       mmc->f_max = CLOCKRATE_MAX;
+
+       /*
+        * We can do SG-DMA, but we don't because we never know how much
+        * data we successfully wrote to the card.
+        */
+       mmc->max_phys_segs = NR_SG;
+
+       /*
+        * Our hardware DMA can handle a maximum of one page per SG entry.
+        */
+       mmc->max_seg_size = PAGE_SIZE;
 
        host = mmc_priv(mmc);
        host->mmc = mmc;
@@ -451,7 +459,7 @@ static int pxamci_probe(struct device *dev)
                         host->pdata->ocr_mask :
                         MMC_VDD_32_33|MMC_VDD_33_34;
 
-       host->sg_cpu = dma_alloc_coherent(dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
+       host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
        if (!host->sg_cpu) {
                ret = -ENOMEM;
                goto out;
@@ -460,8 +468,7 @@ static int pxamci_probe(struct device *dev)
        spin_lock_init(&host->lock);
        host->res = r;
        host->irq = irq;
-       host->imask = TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD|
-                     END_CMD_RES|PRG_DONE|DATA_TRAN_DONE;
+       host->imask = MMC_I_MASK_ALL;
 
        host->base = ioremap(r->start, SZ_4K);
        if (!host->base) {
@@ -478,24 +485,21 @@ static int pxamci_probe(struct device *dev)
        writel(64, host->base + MMC_RESTO);
        writel(host->imask, host->base + MMC_I_MASK);
 
-       pxa_gpio_mode(GPIO6_MMCCLK_MD);
-       pxa_gpio_mode(GPIO8_MMCCS0_MD);
-       pxa_set_cken(CKEN12_MMC, 1);
-
-       host->dma = pxa_request_dma("PXAMCI", DMA_PRIO_LOW, pxamci_dma_irq, host);
+       host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW,
+                                   pxamci_dma_irq, host);
        if (host->dma < 0) {
                ret = -EBUSY;
                goto out;
        }
 
-       ret = request_irq(host->irq, pxamci_irq, 0, "PXAMCI", host);
+       ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host);
        if (ret)
                goto out;
 
-       dev_set_drvdata(dev, mmc);
+       platform_set_drvdata(pdev, mmc);
 
        if (host->pdata && host->pdata->init)
-               host->pdata->init(dev, pxamci_detect_irq, mmc);
+               host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc);
 
        mmc_add_host(mmc);
 
@@ -508,7 +512,7 @@ static int pxamci_probe(struct device *dev)
                if (host->base)
                        iounmap(host->base);
                if (host->sg_cpu)
-                       dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+                       dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
        }
        if (mmc)
                mmc_free_host(mmc);
@@ -516,17 +520,17 @@ static int pxamci_probe(struct device *dev)
        return ret;
 }
 
-static int pxamci_remove(struct device *dev)
+static int pxamci_remove(struct platform_device *pdev)
 {
-       struct mmc_host *mmc = dev_get_drvdata(dev);
+       struct mmc_host *mmc = platform_get_drvdata(pdev);
 
-       dev_set_drvdata(dev, NULL);
+       platform_set_drvdata(pdev, NULL);
 
        if (mmc) {
                struct pxamci_host *host = mmc_priv(mmc);
 
                if (host->pdata && host->pdata->exit)
-                       host->pdata->exit(dev, mmc);
+                       host->pdata->exit(&pdev->dev, mmc);
 
                mmc_remove_host(mmc);
 
@@ -535,14 +539,13 @@ static int pxamci_remove(struct device *dev)
                       END_CMD_RES|PRG_DONE|DATA_TRAN_DONE,
                       host->base + MMC_I_MASK);
 
-               pxa_set_cken(CKEN12_MMC, 0);
+               DRCMRRXMMC = 0;
+               DRCMRTXMMC = 0;
 
                free_irq(host->irq, host);
                pxa_free_dma(host->dma);
                iounmap(host->base);
-               dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
-
-               pxa_set_cken(CKEN12_MMC, 0);
+               dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
 
                release_resource(host->res);
 
@@ -552,23 +555,23 @@ static int pxamci_remove(struct device *dev)
 }
 
 #ifdef CONFIG_PM
-static int pxamci_suspend(struct device *dev, u32 state, u32 level)
+static int pxamci_suspend(struct platform_device *dev, pm_message_t state)
 {
-       struct mmc_host *mmc = dev_get_drvdata(dev);
+       struct mmc_host *mmc = platform_get_drvdata(dev);
        int ret = 0;
 
-       if (mmc && level == SUSPEND_DISABLE)
+       if (mmc)
                ret = mmc_suspend_host(mmc, state);
 
        return ret;
 }
 
-static int pxamci_resume(struct device *dev, u32 level)
+static int pxamci_resume(struct platform_device *dev)
 {
-       struct mmc_host *mmc = dev_get_drvdata(dev);
+       struct mmc_host *mmc = platform_get_drvdata(dev);
        int ret = 0;
 
-       if (mmc && level == RESUME_ENABLE)
+       if (mmc)
                ret = mmc_resume_host(mmc);
 
        return ret;
@@ -578,23 +581,24 @@ static int pxamci_resume(struct device *dev, u32 level)
 #define pxamci_resume  NULL
 #endif
 
-static struct device_driver pxamci_driver = {
-       .name           = "pxa2xx-mci",
-       .bus            = &platform_bus_type,
+static struct platform_driver pxamci_driver = {
        .probe          = pxamci_probe,
        .remove         = pxamci_remove,
        .suspend        = pxamci_suspend,
        .resume         = pxamci_resume,
+       .driver         = {
+               .name   = DRIVER_NAME,
+       },
 };
 
 static int __init pxamci_init(void)
 {
-       return driver_register(&pxamci_driver);
+       return platform_driver_register(&pxamci_driver);
 }
 
 static void __exit pxamci_exit(void)
 {
-       driver_unregister(&pxamci_driver);
+       platform_driver_unregister(&pxamci_driver);
 }
 
 module_init(pxamci_init);