Hi all,

This patch will add SDIO support to the DM355 host controller driver.

I have verified this on DM355 EVM board in both DMA and PIO modes. And i have 
used open source libertas driver for verifying the SDIO functionality.

Signed-off-by: Phaneendra Kumar <[email protected]>
-----
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 8907b72..0744059 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -31,6 +31,8 @@
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #include <linux/mmc/mmc.h>
+#include <linux/timer.h>
+#include <linux/mmc/card.h>
 
 #include <mach/mmc.h>
 #include <mach/edma.h>
@@ -65,8 +67,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 */
@@ -133,6 +135,23 @@
 /* MMCSD Init clock in Hz in opendrain mode */
 #define MMCSD_INIT_CLOCK               200000
 
+/* DAVINCI_SDIOCTL definitions */
+#define SDIOCTL_RDWTRQ_SET       (1 << 0)
+#define SDIOCTL_RDWTCR_SET       (1 << 0)
+
+/* DAVINCI_SDIOST0 definitions */
+#define SDIOST0_DAT1_HI                  (1 << 0)
+#define SDIOST0_INTPRD           (1 << 1)
+#define SDIOST0_RDWTST           (1 << 2)
+
+/* DAVINCI_SDIOIEN definitions */
+#define SDIOIEN_IOINTEN                  (1 << 0)
+#define SDIOIEN_RWSEN            (1 << 1)
+
+/* DAVINCI_SDIOIST definitions */
+#define SDIOIST_IOINT            (1 << 0)
+#define SDIOIST_RWS                      (1 << 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
@@ -181,6 +200,10 @@ struct mmc_davinci_host {
        u32 rxdma, txdma;
        bool use_dma;
        bool do_dma;
+       struct timer_list sdio_timer;
+       u32 sdioInt;
+       /* For sdio irq enabling and disabling */
+       spinlock_t sdio_lock;
 
        /* Scatterlist DMA uses one or more parameter RAM entries:
         * the main one (associated with rxdma or txdma) plus zero or
@@ -202,6 +225,41 @@ struct mmc_davinci_host {
        unsigned ns_in_one_cycle;
 };
 
+static void mmc_enable_sdio_irq(struct mmc_host *mmc, int enable);
+
+static inline void davinci_sdio_intr_chck(struct mmc_davinci_host *host)
+{
+       if (host->mmc->caps & MMC_CAP_SDIO_IRQ) {
+               if (!((readl(host->base + DAVINCI_SDIOST0)) & SDIOST0_DAT1_HI)
+                               && host->sdioInt) {
+                       writel(SDIOIST_IOINT, host->base + DAVINCI_SDIOIST);
+                       mmc_signal_sdio_irq(host->mmc);
+               }
+       }
+}
+
+static inline void davinci_cmd_dat_reset(struct mmc_davinci_host *host)
+{
+       u32 temp = 0;
+
+       if (host->mmc->card) {
+               if (mmc_card_sdio(host->mmc->card)) {
+                       temp = readl(host->base + DAVINCI_MMCCTL);
+                       writel(temp | MMCCTL_CMDRST | MMCCTL_DATRST,
+                                       host->base + DAVINCI_MMCCTL);
+
+                       temp &= ~(MMCCTL_CMDRST | MMCCTL_DATRST);
+                       writel(temp, host->base + DAVINCI_MMCCTL);
+               }
+       }
+}
+
+static void davinci_sdio_timer(unsigned long data)
+{
+       struct mmc_davinci_host *host = (struct mmc_davinci_host *)data;
+
+       davinci_sdio_intr_chck(host);
+}
 
 /* PIO only */
 static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host)
@@ -387,6 +445,15 @@ 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)) {
+                       printk(KERN_ERR "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 +731,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
@@ -706,6 +781,8 @@ static void mmc_davinci_request(struct mmc_host *mmc, 
struct mmc_request *req)
                return;
        }
 
+       davinci_cmd_dat_reset(host);
+
        host->do_dma = 0;
        mmc_davinci_prepare_data(host, req);
        mmc_davinci_start_command(host, req->cmd);
@@ -826,12 +903,9 @@ 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);
-
                dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
                             (data->flags & MMC_DATA_WRITE)
                             ? DMA_TO_DEVICE
@@ -839,11 +913,18 @@ 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;
+
+       davinci_cmd_dat_reset(host);
+
        if (!data->stop || (host->cmd && host->cmd->error)) {
                mmc_request_done(host->mmc, data->mrq);
                writel(0, host->base + DAVINCI_MMCIM);
        } else
                mmc_davinci_start_command(host, data->stop);
+
+       davinci_sdio_intr_chck(host);
 }
 
 static void mmc_davinci_cmd_done(struct mmc_davinci_host *host,
@@ -870,6 +951,7 @@ static void mmc_davinci_cmd_done(struct mmc_davinci_host 
*host,
                mmc_request_done(host->mmc, cmd->mrq);
                writel(0, host->base + DAVINCI_MMCIM);
        }
+       davinci_sdio_intr_chck(host);
 }
 
 static void
@@ -895,6 +977,17 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
        int end_transfer = 0;
        struct mmc_data *data = host->data;
 
+       if (host->mmc->caps & MMC_CAP_SDIO_IRQ) {
+               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);
+               }
+       }
+
        if (host->cmd == NULL && host->data == NULL) {
                status = readl(host->base + DAVINCI_MMCST0);
                dev_dbg(mmc_dev(host->mmc),
@@ -904,6 +997,7 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
                return IRQ_NONE;
        }
 
+       davinci_sdio_intr_chck(host);
        status = readl(host->base + DAVINCI_MMCST0);
        qstatus = status;
 
@@ -1031,11 +1125,41 @@ static int mmc_davinci_get_ro(struct mmc_host *mmc)
        return config->get_ro(pdev->id);
 }
 
+static void mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+       struct mmc_davinci_host *host = mmc_priv(mmc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&host->sdio_lock, flags);
+
+       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->sdioInt = 1;
+                       mod_timer(&host->sdio_timer, jiffies + (HZ/100));
+                       writel(readl(host->base + DAVINCI_SDIOIEN) |
+                               SDIOIEN_IOINTEN, host->base + DAVINCI_SDIOIEN);
+               }
+       } else {
+               host->sdioInt = 0;
+               del_timer(&host->sdio_timer);
+               writel(readl(host->base + DAVINCI_SDIOIEN) & ~SDIOIEN_IOINTEN,
+                               host->base + DAVINCI_SDIOIEN);
+       }
+
+       spin_unlock_irqrestore(&host->sdio_lock, flags);
+}
+
 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_enable_sdio_irq,
 };
 
 /*----------------------------------------------------------------------*/
@@ -1132,6 +1256,12 @@ static int __init davinci_mmcsd_probe(struct 
platform_device *pdev)
        /* REVISIT:  someday, support IRQ-driven card detection.  */
        mmc->caps |= MMC_CAP_NEEDS_POLL;
 
+       mmc->caps |= MMC_CAP_SDIO_IRQ;
+       spin_lock_init(&host->sdio_lock);
+       setup_timer(&host->sdio_timer, davinci_sdio_timer,
+                (unsigned long)host);
+       host->sdioInt = 0;
+
        if (!pdata || pdata->wires == 4 || pdata->wires == 0)
                mmc->caps |= MMC_CAP_4_BIT_DATA;

_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to