[PATCH] mmc: add the MMC/SD/SDIO Arasan host controller

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds the MMC/SD/SDIO Arasan host controller.
Advanced DMA, Single DMA and PIO modes are supported.
The former is the default.
This has been tested on the 7108/06 STM platforms.

Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@xxxxxx>
---
 drivers/mmc/host/Kconfig        |    7 +
 drivers/mmc/host/Makefile       |    1 +
 drivers/mmc/host/arasan.c       | 1354 +++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/arasan.h       |  237 +++++++
 include/linux/mmc/arasan_plat.h |   49 ++
 5 files changed, 1648 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/host/arasan.c
 create mode 100644 drivers/mmc/host/arasan.h
 create mode 100644 include/linux/mmc/arasan_plat.h

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index f06d06e..f403f1d 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -432,3 +432,10 @@ config MMC_SH_MMCIF
 	  This selects the MMC Host Interface controler (MMCIF).
 
 	  This driver supports MMCIF in sh7724/sh7757/sh7372.
+
+config MMC_ARASAN
+	tristate "Arasan MMC/SD/SDIO host driver"
+	depends on CPU_SUBTYPE_ST40
+	help
+	  This selects the Arasan MMC/SD/SDIO host controller integrated
+	  in the STMicroelectronics platforms (stx7108 and stx7106).
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e30c2ee..8337f15 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_MMC_CB710)	+= cb710-mmc.o
 obj-$(CONFIG_MMC_VIA_SDMMC)	+= via-sdmmc.o
 obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
+obj-$(CONFIG_MMC_ARASAN)	+= arasan.o
 
 obj-$(CONFIG_MMC_SDHCI_OF)	+= sdhci-of.o
 sdhci-of-y				:= sdhci-of-core.o
diff --git a/drivers/mmc/host/arasan.c b/drivers/mmc/host/arasan.c
new file mode 100644
index 0000000..7dc49ae
--- /dev/null
+++ b/drivers/mmc/host/arasan.c
@@ -0,0 +1,1354 @@
+/*
+ * Arasan MMC/SD/SDIO driver
+ *
+ *  This is the driver for the Arasan MMC/SD/SDIO host controller
+ *  integrated in the STMicroelectronics platforms
+ *
+ * Author: Giuseppe Cavallaro <peppe.cavallaro@xxxxxx>
+ * Copyright (C) 2010 STMicroelectronics Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/mbus.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/irq.h>
+#include <linux/highmem.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/arasan_plat.h>
+
+#include <asm/sizes.h>
+#include <asm/unaligned.h>
+
+#include "arasan.h"
+
+/* To enable more debug information. */
+#undef ARASAN_DEBUG
+/*#define ARASAN_DEBUG*/
+#ifdef ARASAN_DEBUG
+#define DBG(fmt, args...)  pr_info(fmt, ## args)
+#else
+#define DBG(fmt, args...)  do { } while (0)
+#endif
+
+static int maxfreq = ARASAN_CLOCKRATE_MAX;
+module_param(maxfreq, int, S_IRUGO);
+MODULE_PARM_DESC(maxfreq, "Maximum card clock frequency (default 50MHz)");
+
+static unsigned int adma = 1;
+module_param(adma, int, S_IRUGO);
+MODULE_PARM_DESC(adma, "Disable/Enable the Advanced DMA mode");
+
+static unsigned int led;
+module_param(led, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(led, "Enable|Disable LED");
+
+static unsigned int pio;
+module_param(pio, int, S_IRUGO);
+MODULE_PARM_DESC(pio, "PIO mode (no DMA)");
+
+struct arasan_cap {
+	unsigned int timer_freq;
+	unsigned int timer_unit;
+	unsigned int base_clk_sd;
+	unsigned int max_blk_len;
+	unsigned int adma2;
+	unsigned int high_speed;
+	unsigned int sdma;
+	unsigned int suspend;
+	unsigned int voltage33;
+	unsigned int voltage30;
+	unsigned int voltage18;
+	unsigned int int_mode;
+	unsigned int spi;
+	unsigned int spi_block;
+};
+
+struct arasan_host {
+	void __iomem *base;
+	struct mmc_request *mrq;
+	unsigned int intr_en;
+	u8 ctrl;
+	unsigned int sg_frags;
+	struct timer_list timer;
+	struct mmc_host *mmc;
+	struct device *dev;
+	struct resource *res;
+	int irq;
+	struct arasan_cap cap;
+	u8 vdd;
+	unsigned int freq;
+	unsigned int status;
+	unsigned int adma;
+	unsigned int use_pio;
+	u16 pio_blksz;
+	u32 pio_blocks;
+	u32 *pio_blkbuf;
+	spinlock_t lock;
+	struct tasklet_struct card_tasklet;
+	u8 *adma_desc;
+	dma_addr_t adma_addr;
+	int need_poll;
+};
+
+static inline void arsan_sw_reset(struct arasan_host *host, unsigned int flag)
+{
+	/* After completing the reset, wait the HC clears these bits */
+	if (likely(flag == reset_all)) {
+		writeb(ARSAN_RESET_ALL, host->base + ARASAN_SW_RESET);
+		do { } while ((readb(host->base + ARASAN_SW_RESET)) &
+			 ARSAN_RESET_ALL);
+	} else if (flag == reset_cmd_line) {
+		writeb(ARSAN_RESET_CMD_LINE, host->base + ARASAN_SW_RESET);
+		do { } while ((readb(host->base + ARASAN_SW_RESET)) &
+			 ARSAN_RESET_CMD_LINE);
+
+	} else if (flag == reset_dat_line) {
+		writeb(ARSAN_RESET_DAT_LINE, host->base + ARASAN_SW_RESET);
+		do { } while ((readb(host->base + ARASAN_SW_RESET)) &
+			 ARSAN_RESET_DAT_LINE);
+	}
+}
+
+static inline void arsan_hc_version(struct arasan_host *host)
+{
+	u16 version;
+
+	version = readw(host->base + ARASAN_HOST_VERSION);
+	pr_debug("Arasan MMC/SDIO:\n\tHC Vendor Version Number: %d\n",
+		 (version >> 8));
+	pr_debug("\tHC SPEC Version Number: %d\n", (version & 0x00ff));
+}
+
+static void arasan_capabilities(struct arasan_host *host)
+{
+	unsigned int cap;
+	unsigned int max_blk_len;
+
+	cap = readl(host->base + ARASAN_CAPABILITIES);
+
+	pr_debug("\tArasan capabilities: 0x%x\n", cap);
+
+	host->cap.timer_freq = cap & 0x3f;
+	host->cap.timer_unit = (cap >> 7) & 0x1;
+
+	pr_debug("\tTimeout Clock Freq: %d %s\n", host->cap.timer_freq,
+		 host->cap.timer_unit ? "MHz" : "KHz");
+
+	host->cap.base_clk_sd = (cap >> 8) & 0x3f;
+	pr_debug("\tBase Clock Freq for SD: %d MHz\n", host->cap.base_clk_sd);
+
+	max_blk_len = (cap >> 16) & 0x3;
+	switch (max_blk_len) {
+	case 0:
+		host->cap.max_blk_len = 512;
+		break;
+	case 1:
+		host->cap.max_blk_len = 1024;
+		break;
+	case 2:
+		host->cap.max_blk_len = 2048;
+		break;
+	case 3:
+		host->cap.max_blk_len = 4096;
+		break;
+	default:
+		break;
+	}
+	pr_debug("\tMax Block size: %d bytes\n", host->cap.max_blk_len);
+
+	host->cap.adma2 = (cap >> 19) & 0x1;
+	host->cap.high_speed = (cap >> 21) & 0x1;
+	host->cap.sdma = (cap >> 22) & 0x1;
+
+	pr_debug("\tadma2 %s, high speed %s, sdma %s\n",
+		 host->cap.adma2 ? "Yes" : "Not",
+		 host->cap.high_speed ? "Yes" : "Not",
+		 host->cap.sdma ? "Yes" : "Not");
+
+	host->cap.suspend = (cap >> 23) & 0x1;
+	pr_debug("\tsuspend/resume %s suported\n",
+		 host->cap.adma2 ? "is" : "Not");
+
+	/* Disable adma user option if cap not supported. */
+	if (!host->cap.adma2)
+		adma = 0;
+
+	host->cap.voltage33 = (cap >> 24) & 0x1;
+	host->cap.voltage30 = (cap >> 25) & 0x1;
+	host->cap.voltage18 = (cap >> 26) & 0x1;
+	host->cap.int_mode = (cap >> 27) & 0x1;
+	host->cap.spi = (cap >> 29) & 0x1;
+	host->cap.spi_block = (cap >> 30) & 0x1;
+
+	if (host->cap.voltage33)
+		pr_debug("\t3.3V voltage suported\n");
+	if (host->cap.voltage30)
+		pr_debug("\t3.0V voltage suported\n");
+	if (host->cap.voltage18)
+		pr_debug("\t1.8V voltage suported\n");
+
+	if (host->cap.int_mode)
+		pr_debug("\tInterrupt Mode supported\n");
+	if (host->cap.spi)
+		pr_debug("\tSPI Mode supported\n");
+	if (host->cap.spi_block)
+		pr_debug("\tSPI Block Mode supported\n");
+}
+
+static void arasan_ctrl_led(struct arasan_host *host, unsigned int flag)
+{
+	if (led) {
+		u8 ctrl_reg = readb(host->base + ARASAN_HOST_CTRL);
+
+		if (flag)
+			ctrl_reg |= ARASAN_HOST_CTRL_LED;
+		else
+			ctrl_reg &= ~ARASAN_HOST_CTRL_LED;
+
+		host->ctrl = ctrl_reg;
+		writeb(host->ctrl, host->base + ARASAN_HOST_CTRL);
+	}
+}
+
+static inline void arasan_set_interrupts(struct arasan_host *host)
+{
+	host->intr_en = ARASAN_IRQ_DEFAULT_MASK;
+	writel(host->intr_en, host->base + ARASAN_NORMAL_INT_STATUS_EN);
+	writel(host->intr_en, host->base + ARASAN_NORMAL_INT_SIGN_EN);
+}
+
+static inline void arasan_clear_interrupts(struct arasan_host *host)
+{
+	writel(0, host->base + ARASAN_NORMAL_INT_STATUS_EN);
+	writel(0, host->base + ARASAN_ERR_INT_STATUS_EN);
+	writel(0, host->base + ARASAN_NORMAL_INT_SIGN_EN);
+}
+
+static void arasan_power_set(struct arasan_host *host, unsigned int pwr, u8 vdd)
+{
+	u8 pwr_reg;
+
+	pwr_reg = readb(host->base + ARASAN_PWR_CTRL);
+
+	host->vdd = (1 << vdd);
+
+	if (pwr) {
+		pwr_reg &= 0xf1;
+
+		if ((host->vdd & MMC_VDD_165_195) && host->cap.voltage18)
+			pwr_reg |= ARASAN_PWR_BUS_VOLTAGE_18;
+		else if ((host->vdd & MMC_VDD_29_30) && host->cap.voltage30)
+			pwr_reg |= ARASAN_PWR_BUS_VOLTAGE_30;
+		else if ((host->vdd & MMC_VDD_32_33) && host->cap.voltage33)
+			pwr_reg |= ARASAN_PWR_BUS_VOLTAGE_33;
+
+		pwr_reg |= ARASAN_PWR_CTRL_UP;
+	} else
+		pwr_reg &= ~ARASAN_PWR_CTRL_UP;
+
+	DBG("%s: pwr_reg 0x%x, host->vdd = 0x%x\n", __func__, pwr_reg,
+	    host->vdd);
+	writeb(pwr_reg, host->base + ARASAN_PWR_CTRL);
+}
+
+static int arasan_test_card(struct arasan_host *host)
+{
+	unsigned int ret = 0;
+	u32 present = readl(host->base + ARASAN_PRESENT_STATE);
+	if (likely(!(present & ARASAN_PRESENT_STATE_CARD_PRESENT)))
+		ret = -1;
+
+#ifdef ARASAN_DEBUG
+	if (present & ARASAN_PRESENT_STATE_CARD_STABLE)
+		pr_info("\tcard stable...");
+	if (!(present & ARASAN_PRESENT_STATE_WR_EN))
+		pr_info("\tcard Write protected...");
+	if (present & ARASAN_PRESENT_STATE_BUFFER_RD_EN)
+		pr_info("\tPIO Read Enable...");
+	if (present & ARASAN_PRESENT_STATE_BUFFER_WR_EN)
+		pr_info("\tPIO Write Enable...");
+	if (present & ARASAN_PRESENT_STATE_RD_ACTIVE)
+		pr_info("\tRead Xfer data...");
+	if (present & ARASAN_PRESENT_STATE_WR_ACTIVE)
+		pr_info("\tWrite Xfer data...");
+	if (present & ARASAN_PRESENT_STATE_DAT_ACTIVE)
+		pr_info("\tDAT line active...");
+#endif
+	return ret;
+}
+static void arasan_set_clock(struct arasan_host *host, unsigned int freq)
+{
+	u16 clock = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	if ((host->freq != freq) && (freq)) {
+		u16 divisor;
+
+		/* Ensure clock is off before making any changes */
+		writew(clock, host->base + ARASAN_CLOCK_CTRL);
+
+		/* core checks if this is a good freq < max_freq */
+		host->freq = freq;
+
+		DBG("%s:\n\tnew freq %d", __func__, host->freq);
+
+		/* Work out divisor for specified clock frequency */
+		for (divisor = 1; divisor <= 256; divisor *= 2)
+			/* Find first divisor producing a frequency less
+			 * than or equal to MHz */
+			if ((maxfreq / divisor) <= freq)
+				break;
+
+		DBG("\tdivisor %d", divisor);
+		/* Set the clock divisor and enable the internal clock */
+		clock = divisor << (ARASAN_CLOCK_CTRL_SDCLK_SHIFT);
+		clock &= ARASAN_CLOCK_CTRL_SDCLK_MASK;
+		clock |= ARASAN_CLOCK_CTRL_ICLK_ENABLE;
+		writew(clock, host->base + ARASAN_CLOCK_CTRL);
+
+		/* Busy wait for the clock to become stable */
+		do { } while (((readw(host->base + ARASAN_CLOCK_CTRL)) &
+			  ARASAN_CLOCK_CTRL_ICLK_STABLE) == 0);
+
+		/* Enable the SD clock */
+		clock |= ARASAN_CLOCK_CTRL_SDCLK_ENABLE;
+		writew(clock, host->base + ARASAN_CLOCK_CTRL);
+
+		DBG("\tclk ctrl reg. [0x%x]\n",
+		    (unsigned int)readw(host->base + ARASAN_CLOCK_CTRL));
+	}
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/* Read the response from the card */
+static void arasan_get_resp(struct mmc_command *cmd, struct arasan_host *host)
+{
+	unsigned int i;
+	unsigned int resp[4];
+
+	for (i = 0; i < 4; i++)
+		resp[i] = readl(host->base + ARASAN_RSP(i));
+
+	if (cmd->flags & MMC_RSP_136) {
+		cmd->resp[3] = (resp[0] << 8);
+		cmd->resp[2] = (resp[0] >> 24) | (resp[1] << 8);
+		cmd->resp[1] = (resp[1] >> 24) | (resp[2] << 8);
+		cmd->resp[0] = (resp[2] >> 24) | (resp[3] << 8);
+	} else {
+		cmd->resp[0] = resp[0];
+		cmd->resp[1] = resp[1];
+	}
+
+	DBG("%s: resp length %s\n-(CMD%u):\n %08x %08x %08x %08x\n"
+	    "-RAW reg:\n %08x %08x %08x %08x\n",
+	    __func__, (cmd->flags & MMC_RSP_136) ? "136" : "48", cmd->opcode,
+	    cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3],
+	    resp[0], resp[1], resp[2], resp[3]);
+}
+
+static void arasan_read_block_pio(struct arasan_host *host)
+{
+	unsigned long flags;
+	u16 blksz;
+
+	DBG("\tPIO reading\n");
+
+	local_irq_save(flags);
+
+	for (blksz = host->pio_blksz; blksz > 0; blksz -= 4) {
+		*host->pio_blkbuf =
+		    readl(host->base + ARASAN_BUFF);
+		host->pio_blkbuf++;
+	}
+
+	local_irq_restore(flags);
+}
+
+static void arasan_write_block_pio(struct arasan_host *host)
+{
+	unsigned long flags;
+	u16 blksz;
+
+	DBG("\tPIO writing\n");
+	local_irq_save(flags);
+
+	for (blksz = host->pio_blksz; blksz > 0; blksz -= 4) {
+		writel(*host->pio_blkbuf,
+		       host->base + ARASAN_BUFF);
+		host->pio_blkbuf++;
+	}
+
+	local_irq_restore(flags);
+}
+
+static void arasan_data_pio(struct arasan_host *host)
+{
+	if (host->pio_blocks == 0)
+		return;
+
+	if (host->status == STATE_DATA_READ) {
+		while (readl(host->base + ARASAN_PRESENT_STATE) &
+			     ARASAN_PRESENT_STATE_BUFFER_RD_EN) {
+
+			arasan_read_block_pio(host);
+
+			host->pio_blocks--;
+			if (host->pio_blocks == 0)
+				break;
+		}
+
+	} else {
+		while (readl(host->base + ARASAN_PRESENT_STATE) &
+			     ARASAN_PRESENT_STATE_BUFFER_WR_EN) {
+
+			arasan_write_block_pio(host);
+
+			host->pio_blocks--;
+			if (host->pio_blocks == 0)
+				break;
+		}
+	}
+	DBG("\tPIO transfer complete.\n");
+}
+
+static void arasan_start_cmd(struct arasan_host *host, struct mmc_command *cmd)
+{
+	u16 cmdreg = 0;
+
+	/* Command Request */
+	cmdreg = ARASAN_CMD_INDEX(cmd->opcode);
+	DBG("%s: cmd type %04x,  CMD%d\n", __func__,
+	    mmc_resp_type(cmd), cmd->opcode);
+
+	if (cmd->flags & MMC_RSP_BUSY) {
+		cmdreg |= ARASAN_CMD_RSP_48BUSY;
+		DBG("\tResponse length 48 check Busy.\n");
+	} else if (cmd->flags & MMC_RSP_136) {
+		cmdreg |= ARASAN_CMD_RSP_136;
+		DBG("\tResponse length 136\n");
+	} else if (cmd->flags & MMC_RSP_PRESENT) {
+		cmdreg |= ARASAN_CMD_RSP_48;
+		DBG("\tResponse length 48\n");
+	} else {
+		cmdreg |= ARASAN_CMD_RSP_NONE;
+		DBG("\tNo Response\n");
+	}
+
+	if (cmd->flags & MMC_RSP_CRC) {
+		cmdreg |= ARASAN_CMD_CHECK_CMDCRC;
+		DBG("\tCheck the CRC field in the response\n");
+	}
+	if (cmd->flags & MMC_RSP_OPCODE) {
+		cmdreg |= ARASAN_CMD_INDX_CHECK;
+		DBG("\tCheck the Index field in the response\n");
+	}
+
+	/* Wait until the CMD line is not in use */
+	do { } while ((readl(host->base + ARASAN_PRESENT_STATE)) &
+		 ARASAN_PRESENT_STATE_CMD_INHIBIT);
+
+	/* Set the argument register */
+	writel(cmd->arg, host->base + ARASAN_ARG);
+
+	/* Data present and must be transferred */
+	if (likely(host->mrq->data)) {
+		cmdreg |= ARASAN_CMD_DATA_PRESENT;
+		if (cmd->flags & MMC_RSP_BUSY)
+			/* Wait for data inhibit */
+			do { } while ((readl(host->base +
+					ARASAN_PRESENT_STATE)) &
+				 ARASAN_PRESENT_STATE_DAT_INHIBIT);
+	}
+
+	/* Write the Command */
+	writew(cmdreg, host->base + ARASAN_CMD);
+
+	DBG("\tcmd: 0x%x cmd reg: 0x%x - cmd->arg 0x%x, reg 0x%x\n",
+	    cmdreg, readw(host->base + ARASAN_CMD), cmd->arg,
+	    readl(host->base + ARASAN_ARG));
+}
+
+#ifdef ARASAN_DEBUG
+static void arasan_adma_error(struct arasan_host *host)
+{
+	u8 status = readb(host->base + ARASAN_ADMA_ERR_STATUS);
+
+	if (status & ARASAN_ADMA_ERROR_LENGTH)
+		pr_err("-ADMA Length Mismatch Error...");
+
+	if (status & ARASAN_ADMA_ERROR_ST_TFR)
+		pr_err("-Transfer Data Error desc: ");
+	else if (status & ARASAN_ADMA_ERROR_ST_FDS)
+		pr_err("-Fetch Data Error desc: ");
+	else if (status & ARASAN_ADMA_ERROR_ST_STOP)
+		pr_err("-Stop DMA Data Error desc: ");
+
+	pr_err("0x%x", readl(host->base + ARASAN_ADMA_ADDRESS));
+}
+
+static void arasan_adma_dump_desc(u8 *desc)
+{
+	__le32 *dma;
+	__le16 *len;
+	u8 attr;
+
+	pr_info("\tDescriptors:");
+
+	while (1) {
+		dma = (__le32 *) (desc + 4);
+		len = (__le16 *) (desc + 2);
+		attr = *desc;
+
+		pr_info("\t\t%p: Buff 0x%08x, len %d, Attr 0x%02x\n",
+			desc, le32_to_cpu(*dma), le16_to_cpu(*len), attr);
+
+		desc += 8;
+
+		if (attr & 2)	/* END of descriptor */
+			break;
+	}
+}
+#else
+static void arasan_adma_error(struct arasan_host *host)
+{
+}
+
+static void arasan_adma_dump_desc(u8 *desc)
+{
+}
+#endif
+
+static int arasan_init_sg(struct arasan_host *host)
+{
+
+	host->adma_desc = kmalloc((ARASAN_DMA_DESC_NUM * 2 + 1) * 4,
+				  GFP_KERNEL);
+
+	if (unlikely(host->adma_desc == NULL))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void arasan_adma_table_pre(struct arasan_host *host,
+				  struct mmc_data *data)
+{
+	int direction, i;
+	u8 *desc;
+	struct scatterlist *sg;
+	int len;
+	dma_addr_t addr;
+
+	if (host->status == STATE_DATA_READ)
+		direction = DMA_FROM_DEVICE;
+	else
+		direction = DMA_TO_DEVICE;
+
+	DBG("\t%s: sg entries %d\n", __func__, data->sg_len);
+
+	host->sg_frags = dma_map_sg(mmc_dev(host->mmc), data->sg,
+				    data->sg_len, direction);
+	desc = host->adma_desc;
+
+	for_each_sg(data->sg, sg, host->sg_frags, i) {
+		addr = sg_dma_address(sg);
+		len = sg_dma_len(sg);
+
+		DBG("\t\tFrag %d: addr 0x%x, len %d\n", i, addr, len);
+
+		/* Preparing the descriptor */
+		desc[7] = (addr >> 24) & 0xff;
+		desc[6] = (addr >> 16) & 0xff;
+		desc[5] = (addr >> 8) & 0xff;
+		desc[4] = (addr >> 0) & 0xff;
+
+		desc[3] = (len >> 8) & 0xff;
+		desc[2] = (len >> 0) & 0xff;
+
+		desc[1] = 0x00;
+		desc[0] = 0x21;
+
+		desc += 8;
+	}
+	desc -= 8;
+	desc[0] = 0x23;
+
+	arasan_adma_dump_desc(host->adma_desc);
+
+	host->adma_addr = dma_map_single(mmc_dev(host->mmc),
+					 host->adma_desc,
+					 (ARASAN_DMA_DESC_NUM * 2 + 1) * 4,
+					 DMA_TO_DEVICE);
+
+	writel(host->adma_addr, host->base + ARASAN_ADMA_ADDRESS);
+}
+
+static void arasan_adma_table_post(struct arasan_host *host,
+				   struct mmc_data *data)
+{
+	int direction;
+
+	if (host->status == STATE_DATA_READ)
+		direction = DMA_FROM_DEVICE;
+	else
+		direction = DMA_TO_DEVICE;
+
+	DBG("\t%s\n", __func__);
+
+	dma_unmap_single(mmc_dev(host->mmc), host->adma_addr,
+			 (ARASAN_DMA_DESC_NUM * 2 + 1) * 4, DMA_TO_DEVICE);
+
+	dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, direction);
+}
+
+static int arasan_setup_data(struct arasan_host *host)
+{
+	u16 blksz;
+	u16 xfer = 0;
+	struct mmc_data *data = host->mrq->data;
+
+	DBG("%s:\n\t%s mode, data dir: %s; Buff=0x%08x,"
+	    "blocks=%d, blksz=%d\n", __func__, host->use_pio ? "PIO" : "DMA",
+	    (data->flags & MMC_DATA_READ) ? "read" : "write",
+	    (unsigned int)sg_virt(data->sg), data->blocks, data->blksz);
+
+	/* Transfer Direction */
+	if (data->flags & MMC_DATA_READ) {
+		xfer |= ARASAN_XFER_DATA_DIR;
+		host->status = STATE_DATA_READ;
+	} else {
+		xfer &= ~ARASAN_XFER_DATA_DIR;
+		host->status = STATE_DATA_WRITE;
+	}
+
+	xfer |= ARASAN_XFER_BLK_COUNT_EN;
+
+	if (data->blocks > 1)
+		xfer |= ARASAN_XFER_MULTI_BLK | ARASAN_XFER_AUTOCMD12;
+
+	/* Set the block size register */
+	blksz = ARASAN_BLOCK_SIZE_SDMA_512KB;
+	blksz |= (data->blksz & ARASAN_BLOCK_SIZE_TRANSFER);
+	blksz |= (data->blksz & 0x1000) ? ARASAN_BLOCK_SIZE_SDMA_8KB : 0;
+
+	writew(blksz, host->base + ARASAN_BLK_SIZE);
+
+	/* Set the block count register */
+	writew(data->blocks, host->base + ARASAN_BLK_COUNT);
+
+	/* PIO mode is used when 'pio' var is set by the user or no
+	 * sdma is available from HC caps. */
+	if (unlikely(host->use_pio || (host->cap.sdma == 0))) {
+		host->pio_blksz = data->blksz;
+		host->pio_blocks = data->blocks;
+		host->pio_blkbuf = sg_virt(data->sg);
+	} else {
+		dma_addr_t phys_addr;
+
+		/* Enable DMA */
+		xfer |= ARASAN_XFER_DMA_EN;
+
+		/* Scatter list init */
+		host->sg_frags = dma_map_sg(mmc_dev(host->mmc), data->sg,
+					    data->sg_len,
+					    (host->status & STATE_DATA_READ) ?
+					    DMA_FROM_DEVICE : DMA_TO_DEVICE);
+
+		phys_addr = sg_dma_address(data->sg);
+
+		if (likely(host->adma)) {
+			/* Set the host control register dma bits for adma
+			 * if supported and enabled by user. */
+			host->ctrl |= ARASAN_HOST_CTRL_ADMA2_32;
+
+			/* Prepare ADMA table */
+			arasan_adma_table_pre(host, data);
+		} else {
+			/* SDMA Mode selected (default mode) */
+			host->ctrl &= ~ARASAN_HOST_CTRL_ADMA2_64;
+
+			writel((unsigned int)phys_addr,
+			       host->base + ARASAN_SDMA_SYS_ADDR);
+		}
+		writeb(host->ctrl, host->base + ARASAN_HOST_CTRL);
+
+	}
+	/* Set the data transfer mode register */
+	writew(xfer, host->base + ARASAN_XFER_MODE);
+
+	DBG("\tHC Reg [xfer 0x%x] [blksz 0x%x] [blkcount 0x%x] [CRTL 0x%x]\n",
+	    readw(host->base + ARASAN_XFER_MODE),
+	    readw(host->base + ARASAN_BLK_SIZE),
+	    readw(host->base + ARASAN_BLK_COUNT),
+	    readb(host->base + ARASAN_HOST_CTRL));
+
+	return 0;
+}
+
+static void arasan_finish_data(struct arasan_host *host)
+{
+	struct mmc_data *data = host->mrq->data;
+
+	DBG("\t%s\n", __func__);
+
+	if (unlikely(host->pio_blkbuf)) {
+		host->pio_blksz = 0;
+		host->pio_blocks = 0;
+		host->pio_blkbuf = NULL;
+	} else {
+		if (likely(host->adma)) {
+			arasan_adma_table_post(host, data);
+		} else {
+			dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+				     host->sg_frags,
+				     (host->status & STATE_DATA_READ) ?
+				     DMA_FROM_DEVICE : DMA_TO_DEVICE);
+		}
+	}
+
+	data->bytes_xfered = data->blocks * data->blksz;
+	host->status = STATE_CMD;
+}
+
+static int arasan_finish_cmd(unsigned int err_status, unsigned int status,
+			     unsigned int opcode)
+{
+	int ret = 0;
+
+	if (unlikely(err_status)) {
+		if (err_status & ARASAN_CMD_TIMEOUT) {
+			DBG("\tcmd_timeout...\n");
+			ret = -ETIMEDOUT;
+		}
+		if (err_status & ARASAN_CMD_CRC_ERROR) {
+			DBG("\tcmd_crc_error...\n");
+			ret = -EILSEQ;
+		}
+		if (err_status & ARASAN_CMD_END_BIT_ERROR) {
+			DBG("\tcmd_end_bit_error...\n");
+			ret = -EILSEQ;
+		}
+		if (err_status & ARASAN_CMD_INDEX_ERROR) {
+			DBG("\tcmd_index_error...\n");
+			ret = -EILSEQ;
+		}
+	}
+	if (likely(status & ARASAN_N_CMD_COMPLETE))
+		DBG("\tCommand (CMD%u) Completed irq...\n", opcode);
+
+	return ret;
+}
+
+/* Enable/Disable Normal and Error interrupts */
+static void aranan_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+	unsigned long flags;
+	struct arasan_host *host = mmc_priv(mmc);
+
+	DBG("%s: %s CARD_IRQ\n", __func__, enable ? "enable" : "disable");
+
+	spin_lock_irqsave(&host->lock, flags);
+	if (enable)
+		host->intr_en = ARASAN_IRQ_DEFAULT_MASK;
+	else
+		host->intr_en = 0;
+
+	writel(host->intr_en, host->base + ARASAN_NORMAL_INT_STATUS_EN);
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void arasan_timeout_timer(unsigned long data)
+{
+	struct arasan_host *host = (struct arasan_host *)data;
+	struct mmc_request *mrq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	if ((host->mrq) && (host->status == STATE_CMD)) {
+		mrq = host->mrq;
+
+		pr_debug("%s: Timeout waiting for hardware interrupt.\n",
+			 mmc_hostname(host->mmc));
+
+		writel(0xffffffff, host->base + ARASAN_NORMAL_INT_STATUS);
+		aranan_enable_sdio_irq(host->mmc, 1);
+
+		if (mrq->data) {
+			arasan_finish_data(host);
+			arsan_sw_reset(host, reset_dat_line);
+			mrq->data->error = -ETIMEDOUT;
+		}
+		if (likely(mrq->cmd)) {
+			mrq->cmd->error = -ETIMEDOUT;
+			arsan_sw_reset(host, reset_cmd_line);
+			arasan_get_resp(mrq->cmd, host);
+		}
+		arasan_ctrl_led(host, 0);
+		host->mrq = NULL;
+		mmc_request_done(host->mmc, mrq);
+	}
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/* Process requests from the MMC layer */
+static void arasan_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct arasan_host *host = mmc_priv(mmc);
+	struct mmc_command *cmd = mrq->cmd;
+	unsigned long flags;
+
+	BUG_ON(host->mrq != NULL);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	DBG(">>> araran_request:\n");
+	/* Check that there is a card in the slot */
+	if (unlikely(arasan_test_card(host) < 0)) {
+		DBG("%s: Error: No card present...\n", mmc_hostname(host->mmc));
+
+		mrq->cmd->error = -ENOMEDIUM;
+		mmc_request_done(mmc, mrq);
+		spin_unlock_irqrestore(&host->lock, flags);
+		return;
+	}
+
+	host->mrq = mrq;
+
+	host->status = STATE_CMD;
+	if (likely(mrq->data))
+		arasan_setup_data(host);
+
+	/* Turn-on/off the LED when send/complete a cmd */
+	arasan_ctrl_led(host, 1);
+
+	arasan_start_cmd(host, cmd);
+
+	mod_timer(&host->timer, jiffies + 5 * HZ);
+
+	DBG("<<< araran_request done!\n");
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int arasan_get_ro(struct mmc_host *mmc)
+{
+	struct arasan_host *host = mmc_priv(mmc);
+
+	u32 ro = readl(host->base + ARASAN_PRESENT_STATE);
+	if (!(ro & ARASAN_PRESENT_STATE_WR_EN))
+		return 1;
+
+	return 0;
+}
+
+/* I/O bus settings (MMC clock/power ...) */
+static void arasan_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct arasan_host *host = mmc_priv(mmc);
+	u8 ctrl_reg = readb(host->base + ARASAN_HOST_CTRL);
+
+	DBG("%s: pwr %d, clk %d, vdd %d, bus_width %d, timing %d\n",
+	    __func__, ios->power_mode, ios->clock, ios->vdd, ios->bus_width,
+	    ios->timing);
+
+	/* Set the power supply mode */
+	if (ios->power_mode == MMC_POWER_OFF)
+		arasan_power_set(host, 0, ios->vdd);
+	else
+		arasan_power_set(host, 1, ios->vdd);
+
+	/* Timing (high speed supported?) */
+	if ((ios->timing == MMC_TIMING_MMC_HS ||
+	     ios->timing == MMC_TIMING_SD_HS) && host->cap.high_speed)
+		ctrl_reg |= ARASAN_HOST_CTRL_HIGH_SPEED;
+
+	/* Clear the current bus width configuration */
+	ctrl_reg &= ~ARASAN_HOST_CTRL_SD_MASK;
+
+	/* Set SD bus bit mode */
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_8:
+		ctrl_reg |= ARASAN_HOST_CTRL_SD8;
+		break;
+	case MMC_BUS_WIDTH_4:
+		ctrl_reg |= ARASAN_HOST_CTRL_SD;
+		break;
+	}
+
+	/* Default to maximum timeout */
+	writeb(0x0e, host->base + ARASAN_TIMEOUT_CTRL);
+
+	/* Disable Card Interrupt in Host in case we change
+	 * the Bus Width. */
+	aranan_enable_sdio_irq(host->mmc, 0);
+
+	host->ctrl = ctrl_reg;
+	writeb(host->ctrl, host->base + ARASAN_HOST_CTRL);
+
+	aranan_enable_sdio_irq(host->mmc, 1);
+
+	/* Set clock */
+	arasan_set_clock(host, ios->clock);
+}
+
+/* Tasklet for Card-detection */
+static void arasan_tasklet_card(unsigned long data)
+{
+	unsigned long flags;
+	struct arasan_host *host = (struct arasan_host *)data;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	if (likely((readl(host->base + ARASAN_PRESENT_STATE) &
+		    ARASAN_PRESENT_STATE_CARD_PRESENT))) {
+		if (host->mrq) {
+			pr_err("%s: Card removed during transfer!\n",
+			       mmc_hostname(host->mmc));
+			/* Reset cmd and dat lines */
+			arsan_sw_reset(host, reset_cmd_line);
+			arsan_sw_reset(host, reset_dat_line);
+
+			if (likely(host->mrq->cmd)) {
+				host->mrq->cmd->error = -ENOMEDIUM;
+				mmc_request_done(host->mmc, host->mrq);
+			}
+		}
+	}
+
+	spin_unlock_irqrestore(&host->lock, flags);
+
+	if (likely(host->mmc))
+		mmc_detect_change(host->mmc, msecs_to_jiffies(200));
+}
+
+static irqreturn_t arasan_irq(int irq, void *dev)
+{
+	struct arasan_host *host = dev;
+	unsigned int status, err_status, handled = 0;
+	struct mmc_command *cmd = NULL;
+	struct mmc_data *data = NULL;
+
+	spin_lock(&host->lock);
+
+	/* Interrupt Status */
+	status = readl(host->base + ARASAN_NORMAL_INT_STATUS);
+	err_status = (status >> 16) & 0xffff;
+
+	DBG("%s: Normal IRQ status  0x%x, Error status 0x%x\n",
+	    __func__, status & 0xffff, err_status);
+
+	if ((!host->need_poll) &&
+	    ((status & ARASAN_N_CARD_REMOVAL) ||
+		    (status & ARASAN_N_CARD_INS)))
+			tasklet_schedule(&host->card_tasklet);
+
+	if (unlikely(!host->mrq))
+		goto out;
+
+	cmd = host->mrq->cmd;
+	data = host->mrq->data;
+
+	cmd->error = 0;
+	/* Check for any CMD interrupts */
+	if (likely(status & ARASAN_INT_CMD_MASK)) {
+
+		cmd->error = arasan_finish_cmd(err_status, status, cmd->opcode);
+		if (cmd->error)
+			arsan_sw_reset(host, reset_cmd_line);
+
+		if ((host->status == STATE_CMD) || cmd->error) {
+			arasan_get_resp(cmd, host);
+
+			handled = 1;
+		}
+	}
+
+	/* Check for any data interrupts */
+	if (likely((status & ARASAN_INT_DATA_MASK)) && data) {
+		data->error = 0;
+		if (unlikely(err_status)) {
+			if (err_status & ARASAN_DATA_TIMEOUT_ERROR) {
+				DBG("\tdata_timeout_error...\n");
+				data->error = -ETIMEDOUT;
+			}
+			if (err_status & ARASAN_DATA_CRC_ERROR) {
+				DBG("\tdata_crc_error...\n");
+				data->error = -EILSEQ;
+			}
+			if (err_status & ARASAN_DATA_END_ERROR) {
+				DBG("\tdata_end_error...\n");
+				data->error = -EILSEQ;
+			}
+			if (err_status & ARASAN_AUTO_CMD12_ERROR) {
+				unsigned int err_cmd12 =
+				    readw(host->base + ARASAN_CMD12_ERR_STATUS);
+
+				DBG("\tc12err 0x%04x\n", err_cmd12);
+
+				if (err_cmd12 & ARASAN_AUTOCMD12_ERR_NOTEXE)
+					data->stop->error = -ENOEXEC;
+
+				if ((err_cmd12 & ARASAN_AUTOCMD12_ERR_TIMEOUT)
+				    && !(err_cmd12 & ARASAN_AUTOCMD12_ERR_CRC))
+					/* Timeout Error */
+					data->stop->error = -ETIMEDOUT;
+				else if (!(err_cmd12 &
+					   ARASAN_AUTOCMD12_ERR_TIMEOUT)
+					 && (err_cmd12 &
+					     ARASAN_AUTOCMD12_ERR_CRC))
+					/* CRC Error */
+					data->stop->error = -EILSEQ;
+				else if ((err_cmd12 &
+					  ARASAN_AUTOCMD12_ERR_TIMEOUT)
+					 && (err_cmd12 &
+					     ARASAN_AUTOCMD12_ERR_CRC))
+					DBG("\tCMD line Conflict\n");
+			}
+			arsan_sw_reset(host, reset_dat_line);
+			handled = 1;
+		} else {
+			if (likely(((status & ARASAN_N_BUFF_READ) ||
+				    status & ARASAN_N_BUFF_WRITE))) {
+				DBG("\tData R/W interrupts...\n");
+				arasan_data_pio(host);
+			}
+
+			if (likely(status & ARASAN_N_DMA_IRQ))
+				DBG("\tDMA interrupts...\n");
+
+			if (likely(status & ARASAN_N_TRANS_COMPLETE)) {
+				DBG("\tData XFER completed interrupts...\n");
+				arasan_finish_data(host);
+				if (data->stop) {
+					u32 opcode = data->stop->opcode;
+					data->stop->error =
+					    arasan_finish_cmd(err_status,
+							      status, opcode);
+					arasan_get_resp(data->stop, host);
+				}
+				handled = 1;
+			}
+		}
+	}
+	if (err_status & ARASAN_ADMA_ERROR) {
+		DBG("\tADMA Error...\n");
+		arasan_adma_error(host);
+		cmd->error = -EIO;
+	}
+	if (err_status & ARASAN_CURRENT_LIMIT_ERROR) {
+		DBG("\tPower Fail...\n");
+		cmd->error = -EIO;
+	}
+
+	if (likely(host->mrq && handled)) {
+		struct mmc_request *mrq = host->mrq;
+
+		arasan_ctrl_led(host, 0);
+
+		del_timer(&host->timer);
+
+		host->mrq = NULL;
+		DBG("\tcalling mmc_request_done...\n");
+		mmc_request_done(host->mmc, mrq);
+	}
+out:
+	DBG("\tclear status and exit...\n");
+	writel(status, host->base + ARASAN_NORMAL_INT_STATUS);
+
+	spin_unlock(&host->lock);
+
+	return IRQ_HANDLED;
+}
+
+static void arasan_setup_hc(struct arasan_host *host)
+{
+	/* Clear all the interrupts before resetting */
+	arasan_clear_interrupts(host);
+
+	/* Reset All and get the HC version */
+	arsan_sw_reset(host, reset_all);
+
+	/* Print HC version and SPEC */
+	arsan_hc_version(host);
+
+	/* Set capabilities and print theri info */
+	arasan_capabilities(host);
+
+	/* Enable interrupts */
+	arasan_set_interrupts(host);
+}
+
+static const struct mmc_host_ops arasan_ops = {
+	.request = arasan_request,
+	.get_ro = arasan_get_ro,
+	.set_ios = arasan_set_ios,
+	.enable_sdio_irq = aranan_enable_sdio_irq,
+};
+
+static int __init arasan_probe(struct platform_device *pdev)
+{
+	struct mmc_host *mmc = NULL;
+	struct arasan_host *host = NULL;
+	const struct arasan_platform_data *arasan_data;
+	struct resource *r;
+	int ret, irq;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	irq = platform_get_irq_byname(pdev, "mmcirq");
+
+	arasan_data = pdev->dev.platform_data;
+
+	if (!r || irq < 0 || !arasan_data)
+		return -ENXIO;
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (!r) {
+		pr_err("%s: ERROR: memory allocation failed\n", __func__);
+		return -EBUSY;
+		goto out;
+	}
+	/* Allocate the mmc_host with private data size */
+	mmc = mmc_alloc_host(sizeof(struct arasan_host), &pdev->dev);
+	if (!mmc) {
+		pr_err("%s: ERROR: mmc_alloc_host failed\n", __func__);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* Verify resource from the platform */
+	ret = arasan_claim_resource(pdev);
+	if (ret < 0)
+		goto out;
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->dev = &pdev->dev;
+	host->res = r;
+
+	host->need_poll = arasan_data->need_poll;
+	if (host->need_poll) {
+		mmc->caps |= MMC_CAP_NEEDS_POLL;
+		DBG("\tHC needs polling to detect the card...");
+	} else
+		/* no set the MMC_CAP_NEEDS_POLL in cap */
+		tasklet_init(&host->card_tasklet, arasan_tasklet_card,
+			     (unsigned long)host);
+
+	host->base = ioremap(r->start, resource_size(r));
+	if (!host->base) {
+		pr_err("%s: ERROR: memory mapping failed\n", __func__);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret =
+	    request_irq(irq, arasan_irq, IRQF_SHARED, ARASAN_DRIVER_NAME, host);
+	if (ret) {
+		pr_err("%s: cannot assign irq %d\n", __func__, irq);
+		goto out;
+	} else
+		host->irq = irq;
+
+	spin_lock_init(&host->lock);
+
+	/* Setup the Host Controller according to its capabilities */
+	arasan_setup_hc(host);
+
+	mmc->ops = &arasan_ops;
+
+	if (host->cap.voltage33)
+		mmc->ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
+	if (host->cap.voltage30)
+		mmc->ocr_avail |= MMC_VDD_29_30;
+	if (host->cap.voltage18)
+		mmc->ocr_avail |= MMC_VDD_165_195;
+
+	mmc->caps = MMC_CAP_SDIO_IRQ;
+	mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
+
+	if (host->cap.high_speed)
+		mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+
+	host->freq = host->cap.timer_freq * 1000000;
+	host->use_pio = pio;
+	mmc->f_max = maxfreq;
+	mmc->f_min = mmc->f_max / 256;
+
+	/*
+	 * Maximum block size. This is specified in the capabilities register.
+	 */
+	mmc->max_blk_size = host->cap.max_blk_len;
+	mmc->max_blk_count = 65535;
+
+	mmc->max_hw_segs = 1;
+	mmc->max_phys_segs = 128;
+	mmc->max_seg_size = 65535;
+	mmc->max_req_size = 524288;
+
+	/* Passing the "pio" option, we force the driver to not
+	 * use any DMA engines. */
+	if (unlikely(host->use_pio)) {
+		adma = 0;
+		pr_debug("\tPIO mode\n");
+	} else {
+		if (likely(adma)) {
+			/* Turn-on the ADMA if supported by the HW
+			 * or Fall back to SDMA in case of failures */
+			pr_debug("\tADMA mode\n");
+			ret = arasan_init_sg(host);
+			if (unlikely(ret)) {
+				pr_warning("\tSG init failed (disable ADMA)\n");
+				adma = 0;
+			} else
+				/* Set the Maximum number of segments
+				 * becasue we can do scatter/gathering in ADMA
+				 * mode. */
+				mmc->max_hw_segs = 128;
+		} else
+			pr_debug("\tSDMA mode\n");
+	}
+	host->adma = adma;
+
+	platform_set_drvdata(pdev, mmc);
+	ret = mmc_add_host(mmc);
+	if (ret)
+		goto out;
+
+	setup_timer(&host->timer, arasan_timeout_timer, (unsigned long)host);
+
+	pr_info("%s: driver initialized... IRQ: %d, Base addr 0x%x\n",
+		mmc_hostname(mmc), irq, (unsigned int)host->base);
+
+#ifdef ARASAN_DEBUG
+	led = 1;
+#endif
+	return 0;
+out:
+	if (host) {
+		if (host->irq)
+			free_irq(host->irq, host);
+		if (host->base)
+			iounmap(host->base);
+	}
+	if (r)
+		release_resource(r);
+	if (mmc)
+		mmc_free_host(mmc);
+
+	return ret;
+}
+
+static int __exit arasan_remove(struct platform_device *pdev)
+{
+	struct mmc_host *mmc = platform_get_drvdata(pdev);
+
+	if (mmc) {
+		struct arasan_host *host = mmc_priv(mmc);
+
+		arasan_clear_interrupts(host);
+		if (!host->need_poll)
+			tasklet_kill(&host->card_tasklet);
+		mmc_remove_host(mmc);
+		free_irq(host->irq, host);
+		arasan_power_set(host, 0, -1);
+		iounmap(host->base);
+		if (likely(host->adma))
+			kfree(host->adma_desc);
+		release_resource(host->res);
+		mmc_free_host(mmc);
+	}
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int arasan_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+	struct arasan_host *host = mmc_priv(mmc);
+	int ret = 0;
+
+	if (mmc && host->cap.suspend)
+		ret = mmc_suspend_host(mmc);
+
+	return ret;
+}
+
+static int arasan_resume(struct platform_device *dev)
+{
+	struct mmc_host *mmc = platform_get_drvdata(dev);
+	struct arasan_host *host = mmc_priv(mmc);
+	int ret = 0;
+
+	if (mmc && host->cap.suspend)
+		ret = mmc_resume_host(mmc);
+
+	return ret;
+}
+#endif
+
+static struct platform_driver arasan_driver = {
+	.remove = __exit_p(arasan_remove),
+#ifdef CONFIG_PM
+	.suspend = arasan_suspend,
+	.resume = arasan_resume,
+#endif
+	.driver = {
+		   .name = ARASAN_DRIVER_NAME,
+		   },
+};
+
+static int __init arasan_init(void)
+{
+	return platform_driver_probe(&arasan_driver, arasan_probe);
+}
+
+static void __exit arasan_exit(void)
+{
+	platform_driver_unregister(&arasan_driver);
+}
+
+#ifndef MODULE
+static int __init arasan_cmdline_opt(char *str)
+{
+	char *opt;
+
+	if (!str || !*str)
+		return -EINVAL;
+
+	while ((opt = strsep(&str, ",")) != NULL) {
+		if (!strncmp(opt, "maxfreq:", 8))
+			strict_strtoul(opt + 8, 0, (unsigned long *)&maxfreq);
+		else if (!strncmp(opt, "adma:", 5))
+			strict_strtoul(opt + 5, 0, (unsigned long *)&adma);
+		else if (!strncmp(opt, "led:", 4))
+			strict_strtoul(opt + 4, 0, (unsigned long *)&led);
+		else if (!strncmp(opt, "pio:", 4))
+			strict_strtoul(opt + 4, 0, (unsigned long *)&pio);
+	}
+	return 0;
+}
+
+__setup("arasanmmc=", arasan_cmdline_opt);
+#endif
+
+module_init(arasan_init);
+module_exit(arasan_exit);
+
+MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@xxxxxx>");
+MODULE_DESCRIPTION("Arasan MMC/SD/SDIO Host Controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/arasan.h b/drivers/mmc/host/arasan.h
new file mode 100644
index 0000000..56a4f8b
--- /dev/null
+++ b/drivers/mmc/host/arasan.h
@@ -0,0 +1,237 @@
+/*
+ * Author: Giuseppe Cavallaro <peppe.cavallaro@xxxxxx>
+ *
+ * Copyright (C) 2010  STMicroelectronics Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ARASAN_H
+#define __ARASAN_H
+
+#define ARASAN_CLOCKRATE_MAX	50000000
+#define ARASAN_DRIVER_NAME	"arasan"
+#define ARASAN_DMA_DESC_NUM	128
+
+/*
+ * Register offsets
+ */
+#define ARASAN_SDMA_SYS_ADDR		0x000
+#define ARASAN_BLK_SIZE			0x004
+#define ARASAN_BLK_COUNT		0x006
+#define ARASAN_ARG			0x008
+#define ARASAN_XFER_MODE		0x00c
+#define ARASAN_CMD			0x00e
+#define ARASAN_RSP(i)			(0x010 + ((i)<<2))
+#define ARASAN_RSP0			0x010
+#define ARASAN_RSP1			0x012
+#define ARASAN_RSP2			0x014
+#define ARASAN_RSP3			0x016
+#define ARASAN_RSP4			0x018
+#define ARASAN_RSP5			0x01a
+#define ARASAN_RSP6			0x01c
+#define ARASAN_RSP7			0x01e
+#define ARASAN_BUFF			0x020
+#define ARASAN_PRESENT_STATE		0x024
+#define ARASAN_HOST_CTRL		0x028
+#define ARASAN_PWR_CTRL			0x029
+#define ARASAN_GAP_CTRL			0x02a
+#define ARASAN_GAP_WAKEUP		0x02b
+#define ARASAN_CLOCK_CTRL		0x02c
+#define ARASAN_TIMEOUT_CTRL		0x02e
+#define ARASAN_SW_RESET			0x02f
+
+#define ARASAN_NORMAL_INT_STATUS	0x030
+#define ARASAN_ERR_INT_STATUS		0x032
+#define ARASAN_NORMAL_INT_STATUS_EN	0x034
+#define ARASAN_ERR_INT_STATUS_EN	0x036
+#define ARASAN_NORMAL_INT_SIGN_EN	0x038
+#define ARASAN_ERR_INT_SIGN_EN		0x03a
+
+#define ARASAN_CMD12_ERR_STATUS		0x03c
+
+#define ARASAN_CAPABILITIES		0x040
+
+#define ARASAN_ADMA_ERR_STATUS		0x054
+#define ARASAN_ADMA_ADDRESS		0x058
+
+#define ARASAN_SPI_INT_SUPPORT		0x0f0
+#define ARASAN_HOST_VERSION		0x0fe
+
+/* Error Interrupt Status Register */
+#define ARASAN_CMD_TIMEOUT		(1 << 0)
+#define ARASAN_CMD_CRC_ERROR		(1 << 1)
+#define ARASAN_CMD_END_BIT_ERROR	(1 << 2)
+#define ARASAN_CMD_INDEX_ERROR		(1 << 3)
+#define ARASAN_DATA_TIMEOUT_ERROR	(1 << 4)
+#define ARASAN_DATA_CRC_ERROR		(1 << 5)
+#define ARASAN_DATA_END_ERROR		(1 << 6)
+#define ARASAN_CURRENT_LIMIT_ERROR	(1 << 7)
+#define ARASAN_AUTO_CMD12_ERROR		(1 << 8)
+#define ARASAN_ADMA_ERROR		(1 << 9)
+#define ARASAN_TARGET_RESP_ERROR	(1 << 12)
+#define ARASAN_CEATA_ERROR		(1 << 13)
+
+/* Error Interrupt Status ENABLE reg. (0- Masked, 1: Enabled) */
+#define ARASAN_E_EN_CMD_TIMEOUT		(1 << 0)
+#define ARASAN_E_EN_CMD_CRC_ERROR	(1 << 1)
+#define ARASAN_E_EN_CMD_END_BIT_ERROR	(1 << 2)
+#define ARASAN_E_EN_CMD_INDEX_ERROR	(1 << 3)
+#define ARASAN_E_EN_DATA_TIMEOUT_ERROR	(1 << 4)
+#define ARASAN_E_EN_DATA_CRC_ERROR	(1 << 5)
+#define ARASAN_E_EN_DATA_END_ERROR	(1 << 6)
+#define ARASAN_E_EN_CURRENT_LIMIT_ERROR	(1 << 7)
+#define ARASAN_E_EN_AUTO_CMD12_ERROR	(1 << 8)
+#define ARASAN_E_EN_ADMA_ERROR		(1 << 9)
+#define ARASAN_E_EN_TARGET_RESP_ERROR	(1 << 12)
+#define ARASAN_E_EN_CEATA_ERROR		(1 << 13)
+
+/* Normal Interrupt Status Register */
+#define ARASAN_N_CMD_COMPLETE		(1 << 0)
+#define ARASAN_N_TRANS_COMPLETE		(1 << 1)
+#define ARASAN_N_BLK_GAP_EVENT		(1 << 2)
+#define ARASAN_N_DMA_IRQ		(1 << 3)
+#define ARASAN_N_BUFF_WRITE		(1 << 4)
+#define ARASAN_N_BUFF_READ		(1 << 5)
+#define ARASAN_N_CARD_INS		(1 << 6)
+#define ARASAN_N_CARD_REMOVAL		(1 << 7)
+#define ARASAN_N_CARD_IRQ		(1 << 8)
+#define ARASAN_N_ERROR_IRQ		(1 << 15)
+
+/* Normal Interrupt Status ENABLE reg. (0- Masked, 1: Enabled) */
+#define ARASAN_N_EN_CMD_COMPLETE	(1 << 0)
+#define ARASAN_N_EN_TRANS_COMPL		(1 << 1)
+#define ARASAN_N_EN_BLOCK_GAP		(1 << 2)
+#define ARASAN_N_EN_DMA_IRQ		(1 << 3)
+#define ARASAN_N_EN_BUFF_WRITE		(1 << 4)
+#define ARASAN_N_EN_BUFF_READ		(1 << 5)
+#define ARASAN_N_EN_CARD_INS		(1 << 6)
+#define ARASAN_N_EN_CARD_REM		(1 << 7)
+#define ARASAN_N_EN_CARD_IRQ		(1 << 8)
+
+/* Default Enable Normal/Error interrupt mask */
+#define ARASAN_IRQ_DEFAULT_MASK		0x02ff00fb
+
+/* Mask normal and error fields */
+#define ARASAN_INT_DATA_MASK		0x0070003a
+#define ARASAN_INT_CMD_MASK		0x000f0001
+
+/* Command Register */
+#define ARASAN_CMD_RSP_NONE		(0 << 0)
+#define ARASAN_CMD_RSP_136		(1 << 0)
+#define ARASAN_CMD_RSP_48		(2 << 0)
+#define ARASAN_CMD_RSP_48BUSY		(3 << 0)
+#define ARASAN_CMD_CHECK_CMDCRC		(1 << 3)
+#define ARASAN_CMD_INDX_CHECK		(1 << 4)
+#define ARASAN_CMD_DATA_PRESENT		(1 << 5)
+#define ARASAN_COMMAD_TYPE_NORM		(0 << 6)
+#define ARASAN_COMMAD_TYPE_SUSP		(1 << 6)
+#define ARASAN_COMMAD_TYPE_RESU		(2 << 6)
+#define ARASAN_COMMAD_TYPE_ABOR		(3 << 6)
+#define ARASAN_CMD_INDEX(x)		((x) << 8)
+
+/* Transfer Mode Register */
+#define ARASAN_XFER_DMA_EN		(1 << 0)
+#define ARASAN_XFER_BLK_COUNT_EN	(1 << 1)
+#define ARASAN_XFER_AUTOCMD12		(1 << 2)	/* 1: Enable */
+#define ARASAN_XFER_DATA_DIR		(1 << 4)	/* 0: Write, 1: Read */
+#define ARASAN_XFER_MULTI_BLK		(1 << 5)	/* 0: Single 1: Multi */
+#define ARASAN_XFER_SPI_MODE		(1 << 7)	/* 1: SPI 0: SD Mode */
+
+enum xfer_dat_cmd_status {
+	STATE_CMD = 0,
+	STATE_DATA_WRITE = 1,
+	STATE_DATA_READ = 2,
+	STATE_DATA_STOP = 2,
+};
+
+/* Software Reset */
+#define ARSAN_RESET_ALL			0x1
+#define ARSAN_RESET_CMD_LINE		0x2
+#define ARSAN_RESET_DAT_LINE		0x4
+
+enum sw_reset_cmd {
+	reset_all = 0,
+	reset_cmd_line = 1,
+	reset_dat_line = 2,
+};
+
+/* Host Control Register */
+#define ARASAN_HOST_CTRL_LED		(1 << 0)
+#define ARASAN_HOST_CTRL_SD		(1 << 1)	/* 1: 4 bit mode */
+#define ARASAN_HOST_CTRL_HIGH_SPEED	(1 << 2)
+#define ARASAN_HOST_CTRL_SDMA_SEL	(0 << 3)
+#define ARASAN_HOST_CTRL_ADMA1		(1 << 3)
+#define ARASAN_HOST_CTRL_ADMA2_32	(2 << 3)
+#define ARASAN_HOST_CTRL_ADMA2_64	(3 << 3)
+#define ARASAN_HOST_CTRL_SD8		(1 << 5)
+#define ARASAN_HOST_CTRL_CARD_LEV_TEST	(1 << 6)
+#define ARASAN_HOST_CTRL_CARD_SIG_TEST	(1 << 7)
+
+#define ARASAN_HOST_CTRL_SD_MASK	0x22
+
+/* Clock Control Register */
+#define ARASAN_CLOCK_CTRL_SDCLK_MASK	0xff00
+#define ARASAN_CLOCK_CTRL_SDCLK_SHIFT	7
+#define ARASAN_CLOCK_CTRL_SDCLK_256	0x8000
+#define ARASAN_CLOCK_CTRL_SDCLK_128	0x4000
+#define ARASAN_CLOCK_CTRL_SDCLK_64	0x2000
+#define ARASAN_CLOCK_CTRL_SDCLK_32	0x1000
+#define ARASAN_CLOCK_CTRL_SDCLK_16	0x0800
+#define ARASAN_CLOCK_CTRL_SDCLK_8	0x0400
+#define ARASAN_CLOCK_CTRL_SDCLK_4	0x0200
+#define ARASAN_CLOCK_CTRL_SDCLK_2	0x0100
+#define ARASAN_CLOCK_CTRL_SDCLK_1	0x0000
+#define ARASAN_CLOCK_CTRL_SDCLK_ENABLE	(1 << 2)
+#define ARASAN_CLOCK_CTRL_ICLK_STABLE	(1 << 1)
+#define ARASAN_CLOCK_CTRL_ICLK_ENABLE	(1 << 0)
+
+/* Power Control Register */
+#define ARASAN_PWR_CTRL_UP		(1 << 0)	/* 1: Power-On */
+#define ARASAN_PWR_BUS_VOLTAGE_33	(7 << 1)
+#define ARASAN_PWR_BUS_VOLTAGE_30	(6 << 1)
+#define ARASAN_PWR_BUS_VOLTAGE_18	(5 << 1)
+
+/* CMD12 error status bits */
+#define ARASAN_AUTOCMD12_ERR_NOTEXE	(1 << 0)
+#define ARASAN_AUTOCMD12_ERR_TIMEOUT	(1 << 1)
+#define ARASAN_AUTOCMD12_ERR_CRC	(1 << 2)
+#define ARASAN_AUTOCMD12_ERR_ENDBIT	(1 << 3)
+#define ARASAN_AUTOCMD12_ERR_INDEX	(1 << 4)
+#define ARASAN_AUTOCMD12_ERR_NOT_ISSUED	(1 << 7)
+
+/* Present State Register */
+#define ARASAN_PRESENT_STATE_DAT7_4       0x1e000000
+#define ARASAN_PRESENT_STATE_CMD_LINE     0x01000000
+#define ARASAN_PRESENT_STATE_DAT3_0       0x00f00000
+#define ARASAN_PRESENT_STATE_WR_EN        0x00080000
+#define ARASAN_PRESENT_STATE_CARD_DETECT  0x00040000
+#define ARASAN_PRESENT_STATE_CARD_STABLE  0x00020000
+#define ARASAN_PRESENT_STATE_CARD_PRESENT 0x00010000
+#define ARASAN_PRESENT_STATE_BUFFER_RD_EN 0x00000800
+#define ARASAN_PRESENT_STATE_BUFFER_WR_EN 0x00000400
+#define ARASAN_PRESENT_STATE_RD_ACTIVE    0x00000200
+#define ARASAN_PRESENT_STATE_WR_ACTIVE    0x00000100
+#define ARASAN_PRESENT_STATE_DAT_ACTIVE   0x00000004
+#define ARASAN_PRESENT_STATE_DAT_INHIBIT  0x00000002
+#define ARASAN_PRESENT_STATE_CMD_INHIBIT  0x00000001
+
+/* Block size register defines */
+#define ARASAN_BLOCK_SIZE_SDMA_512KB	0x7000
+#define ARASAN_BLOCK_SIZE_SDMA_256KB	0x6000
+#define ARASAN_BLOCK_SIZE_SDMA_128KB	0x5000
+#define ARASAN_BLOCK_SIZE_SDMA_64KB	0x4000
+#define ARASAN_BLOCK_SIZE_SDMA_32KB	0x3000
+#define ARASAN_BLOCK_SIZE_SDMA_16KB	0x2000
+#define ARASAN_BLOCK_SIZE_SDMA_8KB	0x1000
+#define ARASAN_BLOCK_SIZE_SDMA_4KB	0x0000
+#define ARASAN_BLOCK_SIZE_TRANSFER	0x0fff
+
+/* ADMA Error Status Register */
+#define ARASAN_ADMA_ERROR_LENGTH          0x04
+#define ARASAN_ADMA_ERROR_ST_TFR          0x03
+#define ARASAN_ADMA_ERROR_ST_FDS          0x01
+#define ARASAN_ADMA_ERROR_ST_STOP         0x00
+#endif
diff --git a/include/linux/mmc/arasan_plat.h b/include/linux/mmc/arasan_plat.h
new file mode 100644
index 0000000..9e16287
--- /dev/null
+++ b/include/linux/mmc/arasan_plat.h
@@ -0,0 +1,49 @@
+/*
+ * Author: Giuseppe Cavallaro <peppe.cavallaro@xxxxxx>
+ *
+ * include/linux/mmc/arsan_plat.h
+ *
+ * platform data for the Arasan MMC/SD/SDI HC driver
+ *
+ * Copyright (C) 2010  STMicroelectronics Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ */
+
+#ifndef __ARASAN_PLAT_H__
+#define __ARASAN_PLAT_H__
+
+struct arasan_platform_data {
+	unsigned int need_poll;
+#ifdef CONFIG_STM_DRIVERS
+	struct stm_pad_config *pad_config;
+#endif
+};
+
+/* ARASAN Resource configuration */
+#ifdef CONFIG_STM_DRIVERS
+#include <linux/stm/platform.h>
+#include <linux/stm/pad.h>
+static inline int arasan_claim_resource(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct arasan_platform_data *plat_dat = pdev->dev.platform_data;
+
+	/* Pad routing setup required on STM platforms */
+	if (!devm_stm_pad_claim(&pdev->dev, plat_dat->pad_config,
+				dev_name(&pdev->dev))) {
+		pr_err("%s: Failed to request pads!\n", __func__);
+		ret = -ENODEV;
+	}
+	return ret;
+}
+#else
+static inline int arasan_claim_resource(struct platform_device *pdev)
+{
+	return 0;
+}
+#endif
+#endif
-- 
1.5.5.6

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux