mmc: davinci: add support for SDIO irq handling
authorAlagu Sankar <alagusankar@embwise.com>
Mon, 3 Jan 2011 07:13:27 +0000 (12:43 +0530)
committerChris Ball <cjb@laptop.org>
Mon, 10 Jan 2011 00:16:18 +0000 (19:16 -0500)
This patch adds support for handling SDIO interrupt on DaVinci MMC/SD
controller.

The patch has been tested on DM355 and DA850 EVMs with Marvell Libertas
based SDIO wireless LAN card.

Signed-off-by: Alagu Sankar <alagusankar@embwise.com>
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/host/davinci_mmc.c

index b643ddef4f700219c006ffac1d0a3605de20fc70..0076c7448fe638822c72ad7b6b4140cb7cf48aac 100644 (file)
@@ -66,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 */
 #define MMCFIFOCTL_ACCWD_2    (2 << 3) /* access width of 2 bytes    */
 #define MMCFIFOCTL_ACCWD_1    (3 << 3) /* access width of 1 byte     */
 
+/* DAVINCI_SDIOST0 definitions */
+#define SDIOST0_DAT1_HI       BIT(0)
+
+/* DAVINCI_SDIOIEN definitions */
+#define SDIOIEN_IOINTEN       BIT(0)
+
+/* DAVINCI_SDIOIST definitions */
+#define SDIOIST_IOINT         BIT(0)
 
 /* MMCSD Init clock in Hz in opendrain mode */
 #define MMCSD_INIT_CLOCK               200000
@@ -164,7 +172,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 +192,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
@@ -866,6 +875,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.6): 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);
 
@@ -932,6 +954,21 @@ 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 = 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;
@@ -1076,11 +1113,32 @@ 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 = true;
+                       writel(readl(host->base + DAVINCI_SDIOIEN) |
+                              SDIOIEN_IOINTEN, host->base + DAVINCI_SDIOIEN);
+               }
+       } else {
+               host->sdio_int = false;
+               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,
 };
 
 /*----------------------------------------------------------------------*/
@@ -1209,7 +1267,8 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
                host->nr_sg = MAX_NR_SG;
 
        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;
@@ -1270,6 +1329,13 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
        if (ret)
                goto out;
 
+       if (host->sdio_irq >= 0) {
+               ret = request_irq(host->sdio_irq, mmc_davinci_sdio_irq, 0,
+                                 mmc_hostname(mmc), host);
+               if (!ret)
+                       mmc->caps |= MMC_CAP_SDIO_IRQ;
+       }
+
        rename_region(mem, mmc_hostname(mmc));
 
        dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n",
@@ -1313,7 +1379,9 @@ 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);
+               if (host->mmc->caps & MMC_CAP_SDIO_IRQ)
+                       free_irq(host->sdio_irq, host);
 
                davinci_release_dma_channels(host);