diff mbox

Subject: [PATCH] Davinci:DM3xx: SDIO support for DM3xx

Message ID 54d94f230908060805v2bc8cefai81f567116209b7dc@mail.gmail.com (mailing list archive)
State Rejected
Headers show

Commit Message

Phaneendra kumar Aug. 6, 2009, 3:05 p.m. UTC
Fixed various issues related to SDIO interrupt handling and verified the
functionality on DM355EVM and DM365EVM, which were also later pointed by
David Brownell.

Signed-off-by: phani@embwise.com
---
 drivers/mmc/host/davinci_mmc.c |  285
+++++++++++++++++++++++++++++-----------
 1 files changed, 206 insertions(+), 79 deletions(-)

          * addresses) might be presented too.
@@ -664,6 +698,14 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host,
struct mmc_request *req)
     host->buffer = NULL;
     host->bytes_left = data->blocks * data->blksz;

+    if (host->mmc->card) {
+        if (mmc_card_sdio(host->mmc->card)) {
+            if ((data->blksz == 64)) {
+                mdelay(5);
+            }
+        }
+    }
+
     /* For now we try to use DMA whenever we won't need partial FIFO
      * reads or writes, either for the whole transfer (as tested here)
      * or for any individual scatterlist segment (tested when we call
@@ -826,12 +868,17 @@ static void mmc_davinci_set_ios(struct mmc_host *mmc,
struct mmc_ios *ios)
 static void
 mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data)
 {
-    host->data = NULL;
-    host->data_dir = DAVINCI_MMC_DATADIR_NONE;
+    davinci_abort_dma(host);

-    if (host->do_dma) {
-        davinci_abort_dma(host);
+    if (host->mmc->caps & MMC_CAP_SDIO_IRQ) {
+        if (host->sdio_int && (!((readl(host->base + DAVINCI_SDIOST0))
+                    & SDIOST0_DAT1_HI))) {
+            writel(SDIOIST_IOINT, host->base + DAVINCI_SDIOIST);
+            mmc_signal_sdio_irq(host->mmc);
+        }
+    }

+    if (host->do_dma) {
         dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
                  (data->flags & MMC_DATA_WRITE)
                  ? DMA_TO_DEVICE
@@ -839,6 +886,9 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host,
struct mmc_data *data)
         host->do_dma = false;
     }

+    host->data = NULL;
+    host->data_dir = DAVINCI_MMC_DATADIR_NONE;
+
     if (!data->stop || (host->cmd && host->cmd->error)) {
         mmc_request_done(host->mmc, data->mrq);
         writel(0, host->base + DAVINCI_MMCIM);
@@ -887,6 +937,22 @@ davinci_abort_data(struct mmc_davinci_host *host,
struct mmc_data *data)
     writel(temp, host->base + DAVINCI_MMCCTL);
 }

+static irqreturn_t mmc_davinci_sdio_irq(int irq, void *dev_id)
+{
+    struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id;
+    unsigned int status;
+
+    status = readl(host->base + DAVINCI_SDIOIST);
+    if (status & SDIOIST_IOINT) {
+        dev_dbg(mmc_dev(host->mmc),
+                "SDIO interrupt status %x\n", status);
+        writel(status | SDIOIST_IOINT,
+                host->base + DAVINCI_SDIOIST);
+        mmc_signal_sdio_irq(host->mmc);
+    }
+    return IRQ_HANDLED;
+}
+
 static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
 {
     struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id;
@@ -907,6 +973,73 @@ static irqreturn_t mmc_davinci_irq(int irq, void
*dev_id)
     status = readl(host->base + DAVINCI_MMCST0);
     qstatus = status;

+    if (qstatus & MMCST0_ERR_MASK) {
+        if (qstatus & MMCST0_TOUTRD) {
+            /* Read data timeout */
+            data->error = -ETIMEDOUT;
+            end_transfer = 1;
+
+            dev_err(mmc_dev(host->mmc),
+                    "read data timeout, status %x\n",
+                    qstatus);
+
+            davinci_abort_data(host, data);
+            goto end_data;
+        }
+
+        if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) {
+            /* Data CRC error */
+            data->error = -EILSEQ;
+            end_transfer = 1;
+
+            /* NOTE:  this controller uses CRCWR to report both CRC
+             * errors and timeouts (on writes).  MMCDRSP values are
+             * only weakly documented, but 0x9f was clearly a
+             * timeout case and the two three-bit patterns in
+             * various SD specs (101, 010) aren't part of it ...
+             */
+            if (qstatus & MMCST0_CRCWR) {
+                u32 temp = readb(host->base + DAVINCI_MMCDRSP);
+
+                if (temp == 0x9f)
+                    data->error = -ETIMEDOUT;
+            }
+            dev_err(mmc_dev(host->mmc), "data %s %s error\n",
+                (qstatus & MMCST0_CRCWR) ? "write" :
+                "read",    (data->error == -ETIMEDOUT) ?
+                "timeout" : "CRC");
+
+            davinci_abort_data(host, data);
+            goto end_data;
+        }
+
+        if (qstatus & MMCST0_TOUTRS) {
+            /* Command timeout */
+            if (host->cmd) {
+                dev_err(mmc_dev(host->mmc),
+                        "CMD%d timeout, status %x\n",
+                        host->cmd->opcode, qstatus);
+                host->cmd->error = -ETIMEDOUT;
+                if (data) {
+                    end_transfer = 1;
+                    davinci_abort_data(host, data);
+                } else
+                    end_command = 1;
+            }
+            goto end_cmd;
+        }
+
+        if (qstatus & MMCST0_CRCRS) {
+            /* Command CRC error */
+            dev_err(mmc_dev(host->mmc), "Command CRC error\n");
+            if (host->cmd) {
+                host->cmd->error = -EILSEQ;
+                end_command = 1;
+            }
+            goto end_cmd;
+        }
+    }
+
     /* handle FIFO first when using PIO for data.
      * bytes_left will decrease to zero as I/O progress and status will
      * read zero over iteration because this controller status
@@ -922,6 +1055,11 @@ static irqreturn_t mmc_davinci_irq(int irq, void
*dev_id)
         qstatus |= status;
     }

+    if (qstatus & MMCST0_RSPDNE) {
+        /* End of command phase */
+        end_command = (int) host->cmd;
+    }
+
     if (qstatus & MMCST0_DATDNE) {
         /* All blocks sent/received, and CRC checks passed */
         if (data != NULL) {
@@ -939,73 +1077,10 @@ static irqreturn_t mmc_davinci_irq(int irq, void
*dev_id)
         }
     }

-    if (qstatus & MMCST0_TOUTRD) {
-        /* Read data timeout */
-        data->error = -ETIMEDOUT;
-        end_transfer = 1;
-
-        dev_dbg(mmc_dev(host->mmc),
-            "read data timeout, status %x\n",
-            qstatus);
-
-        davinci_abort_data(host, data);
-    }
-
-    if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) {
-        /* Data CRC error */
-        data->error = -EILSEQ;
-        end_transfer = 1;
-
-        /* NOTE:  this controller uses CRCWR to report both CRC
-         * errors and timeouts (on writes).  MMCDRSP values are
-         * only weakly documented, but 0x9f was clearly a timeout
-         * case and the two three-bit patterns in various SD specs
-         * (101, 010) aren't part of it ...
-         */
-        if (qstatus & MMCST0_CRCWR) {
-            u32 temp = readb(host->base + DAVINCI_MMCDRSP);
-
-            if (temp == 0x9f)
-                data->error = -ETIMEDOUT;
-        }
-        dev_dbg(mmc_dev(host->mmc), "data %s %s error\n",
-            (qstatus & MMCST0_CRCWR) ? "write" : "read",
-            (data->error == -ETIMEDOUT) ? "timeout" : "CRC");
-
-        davinci_abort_data(host, data);
-    }
-
-    if (qstatus & MMCST0_TOUTRS) {
-        /* Command timeout */
-        if (host->cmd) {
-            dev_dbg(mmc_dev(host->mmc),
-                "CMD%d timeout, status %x\n",
-                host->cmd->opcode, qstatus);
-            host->cmd->error = -ETIMEDOUT;
-            if (data) {
-                end_transfer = 1;
-                davinci_abort_data(host, data);
-            } else
-                end_command = 1;
-        }
-    }
-
-    if (qstatus & MMCST0_CRCRS) {
-        /* Command CRC error */
-        dev_dbg(mmc_dev(host->mmc), "Command CRC error\n");
-        if (host->cmd) {
-            host->cmd->error = -EILSEQ;
-            end_command = 1;
-        }
-    }
-
-    if (qstatus & MMCST0_RSPDNE) {
-        /* End of command phase */
-        end_command = (int) host->cmd;
-    }
-
+end_cmd:
     if (end_command)
         mmc_davinci_cmd_done(host, host->cmd);
+end_data:
     if (end_transfer)
         mmc_davinci_xfer_done(host, data);
     return IRQ_HANDLED;
@@ -1031,11 +1106,34 @@ static int mmc_davinci_get_ro(struct mmc_host *mmc)
     return config->get_ro(pdev->id);
 }

+static void mmc_davinci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+    struct mmc_davinci_host *host = mmc_priv(mmc);
+
+    if (enable) {
+        if (!((readl(host->base + DAVINCI_SDIOST0))
+                & SDIOST0_DAT1_HI)) {
+            writel(SDIOIST_IOINT,
+                    host->base + DAVINCI_SDIOIST);
+            mmc_signal_sdio_irq(host->mmc);
+        }else {
+            host->sdio_int = 1;
+            writel(readl(host->base + DAVINCI_SDIOIEN) |
+                SDIOIEN_IOINTEN, host->base + DAVINCI_SDIOIEN);
+        }
+    } else {
+        host->sdio_int = 0;
+        writel(readl(host->base + DAVINCI_SDIOIEN) & ~SDIOIEN_IOINTEN,
+                host->base + DAVINCI_SDIOIEN);
+    }
+
+}
 static struct mmc_host_ops mmc_davinci_ops = {
     .request    = mmc_davinci_request,
     .set_ios    = mmc_davinci_set_ios,
     .get_cd        = mmc_davinci_get_cd,
     .get_ro        = mmc_davinci_get_ro,
+    .enable_sdio_irq    = mmc_davinci_enable_sdio_irq,
 };

 /*----------------------------------------------------------------------*/
@@ -1072,15 +1170,14 @@ static int __init davinci_mmcsd_probe(struct
platform_device *pdev)
     struct mmc_davinci_host *host = NULL;
     struct mmc_host *mmc = NULL;
     struct resource *r, *mem = NULL;
-    int ret = 0, irq = 0;
+    int ret = 0;
     size_t mem_size;

     /* REVISIT:  when we're fully converted, fail if pdata is NULL */

     ret = -ENODEV;
     r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-    irq = platform_get_irq(pdev, 0);
-    if (!r || irq == NO_IRQ)
+    if (!r)
         goto out;

     ret = -EBUSY;
@@ -1097,6 +1194,16 @@ static int __init davinci_mmcsd_probe(struct
platform_device *pdev)
     host = mmc_priv(mmc);
     host->mmc = mmc;    /* Important */

+    r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+    if (!r)
+        goto out;
+    host->mmc_irq = r->start;
+
+    r = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+    if (!r)
+        goto out;
+    host->sdio_irq = r->start;
+
     r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
     if (!r)
         goto out;
@@ -1124,7 +1231,6 @@ static int __init davinci_mmcsd_probe(struct
platform_device *pdev)
     init_mmcsd_host(host);

     host->use_dma = use_dma;
-    host->irq = irq;

     if (host->use_dma && davinci_acquire_dma_channels(host) != 0)
         host->use_dma = 0;
@@ -1173,10 +1279,22 @@ static int __init davinci_mmcsd_probe(struct
platform_device *pdev)
     if (ret < 0)
         goto out;

-    ret = request_irq(irq, mmc_davinci_irq, 0, mmc_hostname(mmc), host);
+    ret = request_irq(host->mmc_irq, mmc_davinci_irq, 0,
+        mmc_hostname(mmc), host);
     if (ret)
         goto out;

+    if (host->sdio_irq > 0) {
+        ret = request_irq(host->sdio_irq,
+                mmc_davinci_sdio_irq, 0,
+                DM355_SDIO_IRQ(pdev->id), host);
+        if (ret == 0) {
+            mmc->caps |= MMC_CAP_SDIO_IRQ;
+            host->sdio_int = 0;
+        } else
+            goto out;
+    }
+
     rename_region(mem, mmc_hostname(mmc));

     dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n",
@@ -1215,10 +1333,19 @@ static int __exit davinci_mmcsd_remove(struct
platform_device *pdev)

     platform_set_drvdata(pdev, NULL);
     if (host) {
+
+        writel((readl(host->base + DAVINCI_MMCCLK) & ~MMCCLK_CLKEN),
+                host->base + DAVINCI_MMCCLK);
+
         mmc_remove_host(host->mmc);
-        free_irq(host->irq, host);

-        davinci_release_dma_channels(host);
+        free_irq(host->mmc_irq, host);
+
+        if (host->mmc->caps & MMC_CAP_SDIO_IRQ)
+            free_irq(host->sdio_irq, host);
+
+        if (host->use_dma)
+            davinci_release_dma_channels(host);

         clk_disable(host->clk);
         clk_put(host->clk);

Comments

Kevin Hilman Aug. 25, 2009, 3:23 p.m. UTC | #1
PhaneendraKumar A <phani@embwise.com> writes:

> Fixed various issues related to SDIO interrupt handling and verified the
> functionality on DM355EVM and DM365EVM, which were also later pointed by
> David Brownell.
>
> Signed-off-by: phani@embwise.com

patch is line wrapped.

Kevin

> ---
>  drivers/mmc/host/davinci_mmc.c |  285 +++++++++++++++++++++++++++++-----------
>  1 files changed, 206 insertions(+), 79 deletions(-)
>
> diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
> index 8907b72..a701962 100644
> --- a/drivers/mmc/host/davinci_mmc.c
> +++ b/drivers/mmc/host/davinci_mmc.c
> @@ -31,6 +31,7 @@
>  #include <linux/delay.h>
>  #include <linux/dma-mapping.h>
>  #include <linux/mmc/mmc.h>
> +#include <linux/mmc/card.h>
>  
>  #include <mach/mmc.h>
>  #include <mach/edma.h>
> @@ -65,8 +66,8 @@
>  #define DAVINCI_MMCBLNC      0x60
>  #define DAVINCI_SDIOCTL      0x64
>  #define DAVINCI_SDIOST0      0x68
> -#define DAVINCI_SDIOEN       0x6C
> -#define DAVINCI_SDIOST       0x70
> +#define DAVINCI_SDIOIEN      0x6C
> +#define DAVINCI_SDIOIST      0x70
>  #define DAVINCI_MMCFIFOCTL   0x74 /* FIFO Control Register             */
>  
>  /* DAVINCI_MMCCTL definitions */
> @@ -100,6 +101,8 @@
>  #define MMCST0_DATED          BIT(11)    /* DAT3 edge detect */
>  #define MMCST0_TRNDNE         BIT(12)    /* transfer done */
>  
> +#define MMCST0_ERR_MASK         (0x00F8)
> +
>  /* DAVINCI_MMCST1 definitions */
>  #define MMCST1_BUSY           (1 << 0)
>  
> @@ -133,6 +136,23 @@
>  /* MMCSD Init clock in Hz in opendrain mode */
>  #define MMCSD_INIT_CLOCK        200000
>  
> +/* DAVINCI_SDIOCTL definitions */
> +#define SDIOCTL_RDWTRQ_SET      BIT(0)
> +#define SDIOCTL_RDWTCR_SET      BIT(1)
> +
> +/* DAVINCI_SDIOST0 definitions */
> +#define SDIOST0_DAT1_HI          BIT(0)
> +#define SDIOST0_INTPRD          BIT(1)
> +#define SDIOST0_RDWTST          BIT(2)
> +
> +/* DAVINCI_SDIOIEN definitions */
> +#define SDIOIEN_IOINTEN          BIT(0)
> +#define SDIOIEN_RWSEN          BIT(1)
> +
> +/* DAVINCI_SDIOIST definitions */
> +#define SDIOIST_IOINT          BIT(0)
> +#define SDIOIST_RWS              BIT(1)
> +
>  /*
>   * One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units,
>   * and we handle up to NR_SG segments.  MMC_BLOCK_BOUNCE kicks in only
> @@ -145,6 +165,9 @@
>  
>  #define NR_SG        16
>  
> +#define DM355_SDIO_IRQ(deviceId)             \
> +    (((deviceId) == 0) ? "sdio0" : "sdio1")
> +
>  static unsigned rw_threshold = 32;
>  module_param(rw_threshold, uint, S_IRUGO);
>  MODULE_PARM_DESC(rw_threshold,
> @@ -162,7 +185,7 @@ struct mmc_davinci_host {
>      unsigned int mmc_input_clk;
>      void __iomem *base;
>      struct resource *mem_res;
> -    int irq;
> +    int mmc_irq, sdio_irq;
>      unsigned char bus_mode;
>  
>  #define DAVINCI_MMC_DATADIR_NONE    0
> @@ -181,6 +204,7 @@ struct mmc_davinci_host {
>      u32 rxdma, txdma;
>      bool use_dma;
>      bool do_dma;
> +    u32 sdio_int;
>  
>      /* Scatterlist DMA uses one or more parameter RAM entries:
>       * the main one (associated with rxdma or txdma) plus zero or
> @@ -387,6 +411,16 @@ static void mmc_davinci_dma_cb(unsigned channel, u16
> ch_status, void *data)
>      if (DMA_COMPLETE != ch_status) {
>          struct mmc_davinci_host *host = data;
>  
> +        if (!(host->data)) {
> +            dev_warn(mmc_dev(host->mmc),
> +                "DMA Event Miss / NULL Transfr\n");
> +            edma_stop(host->txdma);
> +            edma_clean_channel(host->txdma);
> +            edma_stop(host->rxdma);
> +            edma_clean_channel(host->rxdma);
> +            return;
> +        }
> +
>          /* Currently means:  DMA Event Missed, or "null" transfer
>           * request was seen.  In the future, TC errors (like bad
>           * addresses) might be presented too.
> @@ -664,6 +698,14 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host,
> struct mmc_request *req)
>      host->buffer = NULL;
>      host->bytes_left = data->blocks * data->blksz;
>  
> +    if (host->mmc->card) {
> +        if (mmc_card_sdio(host->mmc->card)) {
> +            if ((data->blksz == 64)) {
> +                mdelay(5);
> +            }
> +        }
> +    }
> +
>      /* For now we try to use DMA whenever we won't need partial FIFO
>       * reads or writes, either for the whole transfer (as tested here)
>       * or for any individual scatterlist segment (tested when we call
> @@ -826,12 +868,17 @@ static void mmc_davinci_set_ios(struct mmc_host *mmc,
> struct mmc_ios *ios)
>  static void
>  mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data)
>  {
> -    host->data = NULL;
> -    host->data_dir = DAVINCI_MMC_DATADIR_NONE;
> +    davinci_abort_dma(host);
>  
> -    if (host->do_dma) {
> -        davinci_abort_dma(host);
> +    if (host->mmc->caps & MMC_CAP_SDIO_IRQ) {
> +        if (host->sdio_int && (!((readl(host->base + DAVINCI_SDIOST0))
> +                    & SDIOST0_DAT1_HI))) {
> +            writel(SDIOIST_IOINT, host->base + DAVINCI_SDIOIST);
> +            mmc_signal_sdio_irq(host->mmc);
> +        }
> +    }
>  
> +    if (host->do_dma) {
>          dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
>                   (data->flags & MMC_DATA_WRITE)
>                   ? DMA_TO_DEVICE
> @@ -839,6 +886,9 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct
> mmc_data *data)
>          host->do_dma = false;
>      }
>  
> +    host->data = NULL;
> +    host->data_dir = DAVINCI_MMC_DATADIR_NONE;
> +
>      if (!data->stop || (host->cmd && host->cmd->error)) {
>          mmc_request_done(host->mmc, data->mrq);
>          writel(0, host->base + DAVINCI_MMCIM);
> @@ -887,6 +937,22 @@ davinci_abort_data(struct mmc_davinci_host *host, struct
> mmc_data *data)
>      writel(temp, host->base + DAVINCI_MMCCTL);
>  }
>  
> +static irqreturn_t mmc_davinci_sdio_irq(int irq, void *dev_id)
> +{
> +    struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id;
> +    unsigned int status;
> +
> +    status = readl(host->base + DAVINCI_SDIOIST);
> +    if (status & SDIOIST_IOINT) {
> +        dev_dbg(mmc_dev(host->mmc),
> +                "SDIO interrupt status %x\n", status);
> +        writel(status | SDIOIST_IOINT,
> +                host->base + DAVINCI_SDIOIST);
> +        mmc_signal_sdio_irq(host->mmc);
> +    }
> +    return IRQ_HANDLED;
> +}
> +
>  static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
>  {
>      struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id;
> @@ -907,6 +973,73 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
>      status = readl(host->base + DAVINCI_MMCST0);
>      qstatus = status;
>  
> +    if (qstatus & MMCST0_ERR_MASK) {
> +        if (qstatus & MMCST0_TOUTRD) {
> +            /* Read data timeout */
> +            data->error = -ETIMEDOUT;
> +            end_transfer = 1;
> +
> +            dev_err(mmc_dev(host->mmc),
> +                    "read data timeout, status %x\n",
> +                    qstatus);
> +
> +            davinci_abort_data(host, data);
> +            goto end_data;
> +        }
> +
> +        if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) {
> +            /* Data CRC error */
> +            data->error = -EILSEQ;
> +            end_transfer = 1;
> +
> +            /* NOTE:  this controller uses CRCWR to report both CRC
> +             * errors and timeouts (on writes).  MMCDRSP values are
> +             * only weakly documented, but 0x9f was clearly a
> +             * timeout case and the two three-bit patterns in
> +             * various SD specs (101, 010) aren't part of it ...
> +             */
> +            if (qstatus & MMCST0_CRCWR) {
> +                u32 temp = readb(host->base + DAVINCI_MMCDRSP);
> +
> +                if (temp == 0x9f)
> +                    data->error = -ETIMEDOUT;
> +            }
> +            dev_err(mmc_dev(host->mmc), "data %s %s error\n",
> +                (qstatus & MMCST0_CRCWR) ? "write" :
> +                "read",    (data->error == -ETIMEDOUT) ?
> +                "timeout" : "CRC");
> +
> +            davinci_abort_data(host, data);
> +            goto end_data;
> +        }
> +
> +        if (qstatus & MMCST0_TOUTRS) {
> +            /* Command timeout */
> +            if (host->cmd) {
> +                dev_err(mmc_dev(host->mmc),
> +                        "CMD%d timeout, status %x\n",
> +                        host->cmd->opcode, qstatus);
> +                host->cmd->error = -ETIMEDOUT;
> +                if (data) {
> +                    end_transfer = 1;
> +                    davinci_abort_data(host, data);
> +                } else
> +                    end_command = 1;
> +            }
> +            goto end_cmd;
> +        }
> +
> +        if (qstatus & MMCST0_CRCRS) {
> +            /* Command CRC error */
> +            dev_err(mmc_dev(host->mmc), "Command CRC error\n");
> +            if (host->cmd) {
> +                host->cmd->error = -EILSEQ;
> +                end_command = 1;
> +            }
> +            goto end_cmd;
> +        }
> +    }
> +
>      /* handle FIFO first when using PIO for data.
>       * bytes_left will decrease to zero as I/O progress and status will
>       * read zero over iteration because this controller status
> @@ -922,6 +1055,11 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
>          qstatus |= status;
>      }
>  
> +    if (qstatus & MMCST0_RSPDNE) {
> +        /* End of command phase */
> +        end_command = (int) host->cmd;
> +    }
> +
>      if (qstatus & MMCST0_DATDNE) {
>          /* All blocks sent/received, and CRC checks passed */
>          if (data != NULL) {
> @@ -939,73 +1077,10 @@ static irqreturn_t mmc_davinci_irq(int irq, void
> *dev_id)
>          }
>      }
>  
> -    if (qstatus & MMCST0_TOUTRD) {
> -        /* Read data timeout */
> -        data->error = -ETIMEDOUT;
> -        end_transfer = 1;
> -
> -        dev_dbg(mmc_dev(host->mmc),
> -            "read data timeout, status %x\n",
> -            qstatus);
> -
> -        davinci_abort_data(host, data);
> -    }
> -
> -    if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) {
> -        /* Data CRC error */
> -        data->error = -EILSEQ;
> -        end_transfer = 1;
> -
> -        /* NOTE:  this controller uses CRCWR to report both CRC
> -         * errors and timeouts (on writes).  MMCDRSP values are
> -         * only weakly documented, but 0x9f was clearly a timeout
> -         * case and the two three-bit patterns in various SD specs
> -         * (101, 010) aren't part of it ...
> -         */
> -        if (qstatus & MMCST0_CRCWR) {
> -            u32 temp = readb(host->base + DAVINCI_MMCDRSP);
> -
> -            if (temp == 0x9f)
> -                data->error = -ETIMEDOUT;
> -        }
> -        dev_dbg(mmc_dev(host->mmc), "data %s %s error\n",
> -            (qstatus & MMCST0_CRCWR) ? "write" : "read",
> -            (data->error == -ETIMEDOUT) ? "timeout" : "CRC");
> -
> -        davinci_abort_data(host, data);
> -    }
> -
> -    if (qstatus & MMCST0_TOUTRS) {
> -        /* Command timeout */
> -        if (host->cmd) {
> -            dev_dbg(mmc_dev(host->mmc),
> -                "CMD%d timeout, status %x\n",
> -                host->cmd->opcode, qstatus);
> -            host->cmd->error = -ETIMEDOUT;
> -            if (data) {
> -                end_transfer = 1;
> -                davinci_abort_data(host, data);
> -            } else
> -                end_command = 1;
> -        }
> -    }
> -
> -    if (qstatus & MMCST0_CRCRS) {
> -        /* Command CRC error */
> -        dev_dbg(mmc_dev(host->mmc), "Command CRC error\n");
> -        if (host->cmd) {
> -            host->cmd->error = -EILSEQ;
> -            end_command = 1;
> -        }
> -    }
> -
> -    if (qstatus & MMCST0_RSPDNE) {
> -        /* End of command phase */
> -        end_command = (int) host->cmd;
> -    }
> -
> +end_cmd:
>      if (end_command)
>          mmc_davinci_cmd_done(host, host->cmd);
> +end_data:
>      if (end_transfer)
>          mmc_davinci_xfer_done(host, data);
>      return IRQ_HANDLED;
> @@ -1031,11 +1106,34 @@ static int mmc_davinci_get_ro(struct mmc_host *mmc)
>      return config->get_ro(pdev->id);
>  }
>  
> +static void mmc_davinci_enable_sdio_irq(struct mmc_host *mmc, int enable)
> +{
> +    struct mmc_davinci_host *host = mmc_priv(mmc);
> +
> +    if (enable) {
> +        if (!((readl(host->base + DAVINCI_SDIOST0))
> +                & SDIOST0_DAT1_HI)) {
> +            writel(SDIOIST_IOINT,
> +                    host->base + DAVINCI_SDIOIST);
> +            mmc_signal_sdio_irq(host->mmc);
> +        }else {
> +            host->sdio_int = 1;
> +            writel(readl(host->base + DAVINCI_SDIOIEN) |
> +                SDIOIEN_IOINTEN, host->base + DAVINCI_SDIOIEN);
> +        }
> +    } else {
> +        host->sdio_int = 0;
> +        writel(readl(host->base + DAVINCI_SDIOIEN) & ~SDIOIEN_IOINTEN,
> +                host->base + DAVINCI_SDIOIEN);
> +    }
> +
> +}
>  static struct mmc_host_ops mmc_davinci_ops = {
>      .request    = mmc_davinci_request,
>      .set_ios    = mmc_davinci_set_ios,
>      .get_cd        = mmc_davinci_get_cd,
>      .get_ro        = mmc_davinci_get_ro,
> +    .enable_sdio_irq    = mmc_davinci_enable_sdio_irq,
>  };
>  
>  /*----------------------------------------------------------------------*/
> @@ -1072,15 +1170,14 @@ static int __init davinci_mmcsd_probe(struct
> platform_device *pdev)
>      struct mmc_davinci_host *host = NULL;
>      struct mmc_host *mmc = NULL;
>      struct resource *r, *mem = NULL;
> -    int ret = 0, irq = 0;
> +    int ret = 0;
>      size_t mem_size;
>  
>      /* REVISIT:  when we're fully converted, fail if pdata is NULL */
>  
>      ret = -ENODEV;
>      r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -    irq = platform_get_irq(pdev, 0);
> -    if (!r || irq == NO_IRQ)
> +    if (!r)
>          goto out;
>  
>      ret = -EBUSY;
> @@ -1097,6 +1194,16 @@ static int __init davinci_mmcsd_probe(struct
> platform_device *pdev)
>      host = mmc_priv(mmc);
>      host->mmc = mmc;    /* Important */
>  
> +    r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +    if (!r)
> +        goto out;
> +    host->mmc_irq = r->start;
> +
> +    r = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
> +    if (!r)
> +        goto out;
> +    host->sdio_irq = r->start;
> +
>      r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
>      if (!r)
>          goto out;
> @@ -1124,7 +1231,6 @@ static int __init davinci_mmcsd_probe(struct
> platform_device *pdev)
>      init_mmcsd_host(host);
>  
>      host->use_dma = use_dma;
> -    host->irq = irq;
>  
>      if (host->use_dma && davinci_acquire_dma_channels(host) != 0)
>          host->use_dma = 0;
> @@ -1173,10 +1279,22 @@ static int __init davinci_mmcsd_probe(struct
> platform_device *pdev)
>      if (ret < 0)
>          goto out;
>  
> -    ret = request_irq(irq, mmc_davinci_irq, 0, mmc_hostname(mmc), host);
> +    ret = request_irq(host->mmc_irq, mmc_davinci_irq, 0,
> +        mmc_hostname(mmc), host);
>      if (ret)
>          goto out;
>  
> +    if (host->sdio_irq > 0) {
> +        ret = request_irq(host->sdio_irq,
> +                mmc_davinci_sdio_irq, 0,
> +                DM355_SDIO_IRQ(pdev->id), host);
> +        if (ret == 0) {
> +            mmc->caps |= MMC_CAP_SDIO_IRQ;
> +            host->sdio_int = 0;
> +        } else
> +            goto out;
> +    }
> +
>      rename_region(mem, mmc_hostname(mmc));
>  
>      dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n",
> @@ -1215,10 +1333,19 @@ static int __exit davinci_mmcsd_remove(struct
> platform_device *pdev)
>  
>      platform_set_drvdata(pdev, NULL);
>      if (host) {
> +
> +        writel((readl(host->base + DAVINCI_MMCCLK) & ~MMCCLK_CLKEN),
> +                host->base + DAVINCI_MMCCLK);
> +
>          mmc_remove_host(host->mmc);
> -        free_irq(host->irq, host);
>  
> -        davinci_release_dma_channels(host);
> +        free_irq(host->mmc_irq, host);
> +
> +        if (host->mmc->caps & MMC_CAP_SDIO_IRQ)
> +            free_irq(host->sdio_irq, host);
> +
> +        if (host->use_dma)
> +            davinci_release_dma_channels(host);
>  
>          clk_disable(host->clk);
>          clk_put(host->clk);
> --
> 1.6.0.4
>
> _______________________________________________
> Davinci-linux-open-source mailing list
> Davinci-linux-open-source@linux.davincidsp.com
> http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
diff mbox

Patch

diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 8907b72..a701962 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -31,6 +31,7 @@ 
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #include <linux/mmc/mmc.h>
+#include <linux/mmc/card.h>

 #include <mach/mmc.h>
 #include <mach/edma.h>
@@ -65,8 +66,8 @@ 
 #define DAVINCI_MMCBLNC      0x60
 #define DAVINCI_SDIOCTL      0x64
 #define DAVINCI_SDIOST0      0x68
-#define DAVINCI_SDIOEN       0x6C
-#define DAVINCI_SDIOST       0x70
+#define DAVINCI_SDIOIEN      0x6C
+#define DAVINCI_SDIOIST      0x70
 #define DAVINCI_MMCFIFOCTL   0x74 /* FIFO Control Register             */

 /* DAVINCI_MMCCTL definitions */
@@ -100,6 +101,8 @@ 
 #define MMCST0_DATED          BIT(11)    /* DAT3 edge detect */
 #define MMCST0_TRNDNE         BIT(12)    /* transfer done */

+#define MMCST0_ERR_MASK         (0x00F8)
+
 /* DAVINCI_MMCST1 definitions */
 #define MMCST1_BUSY           (1 << 0)

@@ -133,6 +136,23 @@ 
 /* MMCSD Init clock in Hz in opendrain mode */
 #define MMCSD_INIT_CLOCK        200000

+/* DAVINCI_SDIOCTL definitions */
+#define SDIOCTL_RDWTRQ_SET      BIT(0)
+#define SDIOCTL_RDWTCR_SET      BIT(1)
+
+/* DAVINCI_SDIOST0 definitions */
+#define SDIOST0_DAT1_HI          BIT(0)
+#define SDIOST0_INTPRD          BIT(1)
+#define SDIOST0_RDWTST          BIT(2)
+
+/* DAVINCI_SDIOIEN definitions */
+#define SDIOIEN_IOINTEN          BIT(0)
+#define SDIOIEN_RWSEN          BIT(1)
+
+/* DAVINCI_SDIOIST definitions */
+#define SDIOIST_IOINT          BIT(0)
+#define SDIOIST_RWS              BIT(1)
+
 /*
  * One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units,
  * and we handle up to NR_SG segments.  MMC_BLOCK_BOUNCE kicks in only
@@ -145,6 +165,9 @@ 

 #define NR_SG        16

+#define DM355_SDIO_IRQ(deviceId)             \
+    (((deviceId) == 0) ? "sdio0" : "sdio1")
+
 static unsigned rw_threshold = 32;
 module_param(rw_threshold, uint, S_IRUGO);
 MODULE_PARM_DESC(rw_threshold,
@@ -162,7 +185,7 @@  struct mmc_davinci_host {
     unsigned int mmc_input_clk;
     void __iomem *base;
     struct resource *mem_res;
-    int irq;
+    int mmc_irq, sdio_irq;
     unsigned char bus_mode;

 #define DAVINCI_MMC_DATADIR_NONE    0
@@ -181,6 +204,7 @@  struct mmc_davinci_host {
     u32 rxdma, txdma;
     bool use_dma;
     bool do_dma;
+    u32 sdio_int;

     /* Scatterlist DMA uses one or more parameter RAM entries:
      * the main one (associated with rxdma or txdma) plus zero or
@@ -387,6 +411,16 @@  static void mmc_davinci_dma_cb(unsigned channel, u16
ch_status, void *data)
     if (DMA_COMPLETE != ch_status) {
         struct mmc_davinci_host *host = data;

+        if (!(host->data)) {
+            dev_warn(mmc_dev(host->mmc),
+                "DMA Event Miss / NULL Transfr\n");
+            edma_stop(host->txdma);
+            edma_clean_channel(host->txdma);
+            edma_stop(host->rxdma);
+            edma_clean_channel(host->rxdma);
+            return;
+        }
+
         /* Currently means:  DMA Event Missed, or "null" transfer
          * request was seen.  In the future, TC errors (like bad