diff mbox

Davinci SDIO Support

Message ID 1268656963-23828-1-git-send-email-alagusankar@embwise.com (mailing list archive)
State Changes Requested
Headers show

Commit Message

alagusankar@embwise.com March 15, 2010, 12:42 p.m. UTC
None
diff mbox

Patch

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);