From: Alagu Sankar <alagusan...@embwise.com>

Added SDIO Support for Davinci Platforms.  This is tested with DM355 and DM365
EVM platforms using Libertas driver with SD8686 and SD8688 SDIO WiFi cards.
There is a hack to support Libertas firmware download. This hack may not be
required for other SDIO cards.

Signed-off-by: alagusan...@embwise.com
---
 drivers/mmc/host/davinci_mmc.c |  131 ++++++++++++++++++++++++++++++++++++----
 1 files changed, 120 insertions(+), 11 deletions(-)

diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 933cd42..2836ad1 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -32,6 +32,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>
@@ -66,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 */
@@ -135,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
@@ -147,6 +165,8 @@
 
 #define NR_SG          16
 
+#define DAVINCI_SDIO_IRQ(dev_id)       (((dev_id) == 0) ? "sdio0" : "sdio1")
+
 static unsigned rw_threshold = 32;
 module_param(rw_threshold, uint, S_IRUGO);
 MODULE_PARM_DESC(rw_threshold,
@@ -164,7 +184,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
@@ -184,6 +204,7 @@ struct mmc_davinci_host {
        u32 rxdma, txdma;
        bool use_dma;
        bool do_dma;
+       bool sdio_int;
 
        /* Scatterlist DMA uses one or more parameter RAM entries:
         * the main one (associated with rxdma or txdma) plus zero or
@@ -670,6 +691,24 @@ mmc_davinci_prepare_data(struct mmc_davinci_host *host, 
struct mmc_request *req)
        host->buffer = NULL;
        host->bytes_left = data->blocks * data->blksz;
 
+    /*
+     * Hack for overcoming the 2ms delay required by Libertas helper
+     * firmware download.  Without this delay the primary firmware load fails
+     * without an error.  Not having the delay does not result in host
+     * controller error (or) helper firmware download error.  But will result
+     * in primary firmware load error.
+     *
+     * May not be required for usage with other SDIO client drivers and
+     * should be removed after identifying the root cause in consultation
+     * with the libertas developers.
+     */
+       if (host->mmc->card) {
+               if (mmc_card_sdio(host->mmc->card)) {
+                       if ((data->blksz == 64))
+                               mdelay(2);
+               }
+       }
+
        /* 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
@@ -862,6 +901,19 @@ mmc_davinci_xfer_done(struct mmc_davinci_host *host, 
struct mmc_data *data)
 {
        host->data = NULL;
 
+       if (host->mmc->caps & MMC_CAP_SDIO_IRQ) {
+               /* SDIO Interrupt Detection work-around as suggested by
+                * Davinci Errata (TMS320DM355 Silicon Revision 1.1 Errata
+                * 2.1.5)
+                * Signal SDIO interrupt only if it is enabled by core
+                */
+               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) {
                davinci_abort_dma(host);
 
@@ -928,6 +980,22 @@ davinci_abort_data(struct mmc_davinci_host *host, struct 
mmc_data *data)
        mmc_davinci_reset_ctrl(host, 0);
 }
 
+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;
@@ -1072,11 +1140,33 @@ 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,
+       .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,
 };
 
 /*----------------------------------------------------------------------*/
@@ -1199,7 +1289,8 @@ static int __init davinci_mmcsd_probe(struct 
platform_device *pdev)
        init_mmcsd_host(host);
 
        host->use_dma = use_dma;
-       host->irq = irq;
+       host->mmc_irq = irq;
+       host->sdio_irq = platform_get_irq(pdev, 1);
 
        if (host->use_dma && davinci_acquire_dma_channels(host) != 0)
                host->use_dma = 0;
@@ -1258,10 +1349,24 @@ 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;
 
+       /* Failures in SDIO IRQ registration are ignored as the driver
+        * can still work in polled mode.
+        */
+       if (host->sdio_irq != NO_IRQ) {
+               ret = request_irq(host->sdio_irq,
+                               mmc_davinci_sdio_irq, 0,
+                               DAVINCI_SDIO_IRQ(pdev->id), host);
+               if (ret == 0) {
+                       mmc->caps |= MMC_CAP_SDIO_IRQ;
+                       host->sdio_int = 0;
+               }
+       }
+
        rename_region(mem, mmc_hostname(mmc));
 
        dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n",
@@ -1305,9 +1410,13 @@ static int __exit davinci_mmcsd_remove(struct 
platform_device *pdev)
                mmc_davinci_cpufreq_deregister(host);
 
                mmc_remove_host(host->mmc);
-               free_irq(host->irq, host);
+               free_irq(host->mmc_irq, host);
 
-               davinci_release_dma_channels(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.6

_______________________________________________
Davinci-linux-open-source mailing list
Davinci-linux-open-source@linux.davincidsp.com
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to