ipq807x: mmc: Added eMMC driver files

Change-Id: Icf1683ebe1c7e99c336d884983215d7b416c988b
Signed-off-by: Vasudevan Murugesan <vmuruges@codeaurora.org>
diff --git a/drivers/mmc/qca_mmc.c b/drivers/mmc/qca_mmc.c
new file mode 100644
index 0000000..1de48dc
--- /dev/null
+++ b/drivers/mmc/qca_mmc.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+*/
+
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <mmc.h>
+#include <part.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/arch-qcom-common/clk.h>
+#include <asm-generic/errno.h>
+#include "../../board/qcom/common/qca_common.h"
+#include "qca_mmc.h"
+
+static inline void qca_reg_wr_delay(qca_mmc  *host)
+{
+	while (readl(host->base + MCI_STATUS2) & MCI_MCLK_REG_WR_ACTIVE);
+}
+
+static inline void
+qca_start_command_exec(qca_mmc  *host, struct mmc_cmd *cmd)
+{
+	unsigned int c = 0;
+	unsigned int arg = cmd->cmdarg;
+
+	writel(MCI_CLEAR_STATIC_MASK, host->base + MMCICLEAR);
+
+	qca_reg_wr_delay(host);
+
+	c |= (cmd->cmdidx | MCI_CPSM_ENABLE);
+
+	if (cmd->resp_type & MMC_RSP_PRESENT) {
+		if (cmd->resp_type & MMC_RSP_136)
+			c |= MCI_CPSM_LONGRSP;
+		c |= MCI_CPSM_RESPONSE;
+	}
+
+	if ((cmd->resp_type & MMC_RSP_R1b) == MMC_RSP_R1b) {
+		c |= MCI_CPSM_PROGENA;
+	}
+
+	writel(arg, host->base + MMCIARGUMENT);
+	writel(c, host->base + MMCICOMMAND);
+	qca_reg_wr_delay(host);
+}
+
+static int
+qca_start_command(qca_mmc *host, struct mmc_cmd *cmd)
+{
+	unsigned int status = 0;
+	int rc = 0;
+
+	qca_start_command_exec(host, cmd);
+
+	while (readl(host->base + MMCISTATUS) & MCI_CMDACTIVE);
+
+	status = readl(host->base + MMCISTATUS);
+
+	if ((cmd->resp_type != MMC_RSP_NONE)) {
+
+		if (status & MCI_CMDTIMEOUT) {
+			rc = TIMEOUT;
+		}
+		/*  MMC_CMD_SEND_OP_COND response doesn't have CRC. */
+		if ((status & MCI_CMDCRCFAIL) && (cmd->cmdidx != MMC_CMD_SEND_OP_COND)) {
+			rc = UNUSABLE_ERR;
+		}
+		cmd->response[0] = readl(host->base + MMCIRESPONSE0);
+		/*
+		 * Read rest of the response registers only if
+		 * long response is expected for this command
+		 */
+		if (cmd->resp_type & MMC_RSP_136) {
+			cmd->response[1] = readl(host->base + MMCIRESPONSE1);
+			cmd->response[2] = readl(host->base + MMCIRESPONSE2);
+			cmd->response[3] = readl(host->base + MMCIRESPONSE3);
+		}
+	}
+
+	writel(MCI_CLEAR_STATIC_MASK, host->base + MMCICLEAR);
+	qca_reg_wr_delay(host);
+
+	return rc;
+}
+
+
+static int
+qca_pio_read(qca_mmc *host, char *buffer, unsigned int remain)
+{
+	unsigned int *ptr = (unsigned int *) buffer;
+	unsigned int status = 0;
+	unsigned int count = 0;
+	unsigned int error = MCI_DATACRCFAIL | MCI_DATATIMEOUT | MCI_RXOVERRUN;
+	unsigned int i;
+
+
+	do {
+		status = readl(host->base + MMCISTATUS);
+		udelay(1);
+
+		if (status & error) {
+			break;
+		}
+
+		if (status & MCI_RXDATAAVLBL ) {
+			unsigned rd_cnt = 1;
+
+			if (status & MCI_RXFIFOHALFFULL) {
+				rd_cnt = MCI_HFIFO_COUNT;
+			}
+
+			for (i = 0; i < rd_cnt; i++) {
+				*ptr = readl(host->base + MMCIFIFO +
+					(count % MCI_FIFOSIZE));
+				ptr++;
+				count += sizeof(unsigned int);
+			}
+
+			if (count == remain)
+				break;
+
+		} else if (status & MCI_DATAEND) {
+			break;
+		}
+	} while (1);
+
+	return count;
+}
+
+static int
+qca_pio_write(qca_mmc *host, const char *buffer,
+		  unsigned int remain)
+{
+	unsigned int *ptr = (unsigned int *) buffer;
+	unsigned int status = 0;
+	unsigned int count = 0;
+	unsigned int error = MCI_DATACRCFAIL | MCI_DATATIMEOUT | MCI_TXUNDERRUN;
+	unsigned int bcnt = 0;
+	unsigned int sz = 0;
+	int i;
+
+	do {
+		status = readl(host->base + MMCISTATUS);
+
+		bcnt = remain - count;
+
+		if (!bcnt)
+			break;
+
+		if ((status & MCI_TXFIFOEMPTY) ||
+			(status & MCI_TXFIFOHALFEMPTY)) {
+
+			sz = ((bcnt >> 2) >  MCI_HFIFO_COUNT) \
+					? MCI_HFIFO_COUNT : (bcnt >> 2);
+
+			for (i = 0; i < sz; i++) {
+				writel(*ptr, host->base + MMCIFIFO);
+				ptr++;
+				count += sizeof(unsigned int);
+			}
+		}
+	} while (1);
+
+	do {
+		status = readl(host->base + MMCISTATUS);
+
+		if (status & error) {
+			count = status;
+			break;
+		}
+	} while (!(status & MCI_DATAEND));
+
+	return count;
+}
+
+static int
+qca_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
+{
+	unsigned int datactrl = 0;
+	qca_mmc *host = mmc->priv;
+	unsigned int xfer_size ;
+	int status = 0;
+
+	if (data) {
+		writel((readl(host->base + MMCICLOCK) | MCI_CLK_FLOWENA),
+						host->base + MMCICLOCK);
+		xfer_size = data->blocksize * data->blocks;
+		datactrl = MCI_DPSM_ENABLE | (data->blocksize << 4);
+		writel(0xffffffff, host->base + MMCIDATATIMER);
+
+		if (data->flags & MMC_DATA_READ)
+			datactrl |= (MCI_DPSM_DIRECTION | MCI_RX_DATA_PEND);
+
+		writel(xfer_size, host->base + MMCIDATALENGTH);
+		writel(datactrl, host->base + MMCIDATACTRL);
+		qca_reg_wr_delay(host);
+
+		status = qca_start_command(host, cmd);
+
+		if (data->flags & MMC_DATA_READ) {
+			qca_pio_read(host, data->dest, xfer_size);
+		} else {
+			qca_pio_write(host, data->src, xfer_size);
+		}
+		writel(0, host->base + MMCIDATACTRL);
+		qca_reg_wr_delay(host);
+	} else if (cmd) {
+		status = qca_start_command(host, cmd);
+	}
+
+	return status;
+}
+
+
+void qca_set_ios(struct mmc *mmc)
+{
+	qca_mmc *host = mmc->priv;
+	u32 clk = 0, pwr = 0;
+	int mode;
+
+	if (mmc->clock <= mmc->f_min) {
+		mode = MMC_IDENTIFY_MODE;
+	} else {
+		mode = MMC_DATA_TRANSFER_MODE;
+	}
+
+	if (mode != host->clk_mode) {
+		host->clk_mode = mode;
+		emmc_clock_config(host->clk_mode);
+	}
+
+	pwr = MCI_PWR_UP | MCI_PWR_ON;
+
+	writel(pwr, host->base + MMCIPOWER);
+	qca_reg_wr_delay(host);
+	clk = readl(host->base + MMCICLOCK);
+
+	clk |= MCI_CLK_ENABLE;
+	clk |= MCI_CLK_SELECTIN;
+	clk |= MCI_CLK_FLOWENA;
+	/* feedback clock */
+	clk |= (2 << 14);
+
+	if (mmc->bus_width == 1) {
+		 clk |= MCI_CLK_WIDEBUS_1;
+	} else if (mmc->bus_width == 4) {
+		clk |= MCI_CLK_WIDEBUS_4;
+	} else if (mmc->bus_width == 8) {
+		 clk |= MCI_CLK_WIDEBUS_8;
+	}
+
+	/* Select free running MCLK as input clock of cm_dll_sdc4 */
+	clk |= (2 << 23);
+
+	/* Don't write into registers if clocks are disabled */
+	writel(clk, host->base + MMCICLOCK);
+	qca_reg_wr_delay(host);
+
+	writel((readl(host->base + MMCICLOCK) | MCI_CLK_PWRSAVE), host->base + MMCICLOCK);
+	qca_reg_wr_delay(host);
+}
+
+int qca_mmc_start (struct mmc *mmc)
+{
+	qca_mmc *host = mmc->priv;
+
+	writel(readl(host->base + MMCIPOWER) | MCI_SW_RST,
+			host->base + MMCIPOWER);
+	qca_reg_wr_delay(host);
+
+	while (readl(host->base + MMCIPOWER) & MCI_SW_RST) {
+		udelay(10);
+	}
+
+	return 0;
+}
+
+int qca_mmc_init(bd_t *bis, qca_mmc *host)
+{
+	struct mmc *mmc;
+
+	mmc = malloc(sizeof(struct mmc));
+	if (!mmc) {
+	    return -ENOMEM;
+	}
+
+	memset(mmc, 0, sizeof(struct mmc));
+
+	sprintf(mmc->name, "qca_mmc");
+	mmc->priv = host;
+	mmc->send_cmd = qca_mmc_send_cmd;
+	mmc->set_ios = qca_set_ios;
+	mmc->init = qca_mmc_start;
+	mmc->getcd = NULL;
+
+	mmc->f_min = 400000;
+	mmc->f_max = 52000000;
+
+	mmc->b_max = 512;
+	/* voltage either 2.7-3.6V or 1.70 -1.95V  */
+	mmc->voltages = 0x40FF8080;
+	mmc->host_caps = MMC_MODE_8BIT;
+	mmc->host_caps |= MMC_MODE_HC;
+
+	mmc_register(mmc);
+	host->mmc = mmc;
+	host->dev_num = mmc->block_dev.dev;
+
+	return 0;
+}
diff --git a/drivers/mmc/qca_mmc.h b/drivers/mmc/qca_mmc.h
new file mode 100644
index 0000000..0fe1842
--- /dev/null
+++ b/drivers/mmc/qca_mmc.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+*/
+
+#ifndef ___QCA_MMC_H_
+#define ___QCA_MMC_H_
+
+#define MMCIPOWER	0x000
+#define MCI_PWR_OFF	0x00
+#define MCI_PWR_UP	0x02
+#define MCI_PWR_ON	0x03
+#define MCI_SW_RST	(1 << 7)
+
+#define MMCICLOCK		0x004
+#define MCI_CLK_ENABLE		(1 << 8)
+#define MCI_CLK_PWRSAVE		(1 << 9)
+#define MCI_CLK_FLOWENA		(1 << 12)
+#define MCI_CLK_SELECTIN	(1 << 15)
+#define MCI_CLK_WIDEBUS_1	(0 << 10)
+#define MCI_CLK_WIDEBUS_4	(2 << 10)
+#define MCI_CLK_WIDEBUS_8	(3 << 10)
+
+
+#define MMCIARGUMENT		0x008
+#define MMCICOMMAND		0x00c
+#define MCI_CPSM_RESPONSE	(1 << 6)
+#define MCI_CPSM_LONGRSP	(1 << 7)
+#define MCI_CPSM_ENABLE		(1 << 10)
+#define MCI_CPSM_PROGENA	(1 << 11)
+
+#define MMCIRESPCMD		0x010
+#define MMCIRESPONSE0		0x014
+#define MMCIRESPONSE1		0x018
+#define MMCIRESPONSE2		0x01c
+#define MMCIRESPONSE3		0x020
+#define MMCIDATATIMER		0x024
+#define MMCIDATALENGTH		0x028
+
+#define MMCIDATACTRL		0x02c
+#define MCI_DPSM_ENABLE		(1 << 0)
+#define MCI_DPSM_DIRECTION	(1 << 1)
+#define MCI_RX_DATA_PEND	(1 << 20)
+
+#define MMCIDATACNT		0x030
+#define MMCISTATUS		0x034
+#define MCI_CMDCRCFAIL		(1 << 0)
+#define MCI_DATACRCFAIL		(1 << 1)
+#define MCI_CMDTIMEOUT		(1 << 2)
+#define MCI_DATATIMEOUT		(1 << 3)
+#define MCI_TXUNDERRUN		(1 << 4)
+#define MCI_RXOVERRUN		(1 << 5)
+#define MCI_CMDRESPEND		(1 << 6)
+#define MCI_CMDSENT		(1 << 7)
+#define MCI_DATAEND		(1 << 8)
+#define MCI_DATABLOCKEND	(1 << 10)
+#define MCI_CMDACTIVE		(1 << 11)
+#define MCI_TXACTIVE		(1 << 12)
+#define MCI_RXACTIVE		(1 << 13)
+#define MCI_TXFIFOHALFEMPTY	(1 << 14)
+#define MCI_RXFIFOHALFFULL	(1 << 15)
+#define MCI_TXFIFOFULL		(1 << 16)
+#define MCI_RXFIFOFULL		(1 << 17)
+#define MCI_TXFIFOEMPTY		(1 << 18)
+#define MCI_RXFIFOEMPTY		(1 << 19)
+#define MCI_TXDATAAVLBL		(1 << 20)
+#define MCI_RXDATAAVLBL		(1 << 21)
+
+#define MMCICLEAR		0x038
+#define MCI_CMDCRCFAILCLR	(1 << 0)
+#define MCI_DATACRCFAILCLR	(1 << 1)
+#define MCI_CMDTIMEOUTCLR	(1 << 2)
+#define MCI_DATATIMEOUTCLR	(1 << 3)
+#define MCI_TXUNDERRUNCLR	(1 << 4)
+#define MCI_RXOVERRUNCLR	(1 << 5)
+#define MCI_CMDRESPENDCLR	(1 << 6)
+#define MCI_CMDSENTCLR		(1 << 7)
+#define MCI_DATAENDCLR		(1 << 8)
+#define MCI_STARTBITERRCLR	(1 << 9)
+#define MCI_DATABLOCKENDCLR	(1 << 10)
+
+#define MCI_SDIOINTRCLR		(1 << 22)
+#define MCI_PROGDONECLR		(1 << 23)
+#define MCI_ATACMDCOMPLCLR 	(1 << 24)
+#define MCI_SDIOINTROPECLR	(1 << 25)
+#define MCI_CCSTIMEOUTCLR	(1 << 26)
+
+#define MCI_CLEAR_STATIC_MASK   \
+	(MCI_CMDCRCFAILCLR|MCI_DATACRCFAILCLR|MCI_CMDTIMEOUTCLR|\
+	MCI_DATATIMEOUTCLR|MCI_TXUNDERRUNCLR|MCI_RXOVERRUNCLR|  \
+	MCI_CMDRESPENDCLR|MCI_CMDSENTCLR|MCI_DATAENDCLR|        \
+	MCI_STARTBITERRCLR|MCI_DATABLOCKENDCLR|MCI_SDIOINTRCLR| \
+	MCI_SDIOINTROPECLR|MCI_PROGDONECLR|MCI_ATACMDCOMPLCLR|  \
+	MCI_CCSTIMEOUTCLR)
+
+#define MCI_STATUS2		0x06C
+#define MCI_MCLK_REG_WR_ACTIVE	(1 << 0)
+
+#define MMCIFIFO		0x080 /* to 0x0bc */
+/*
+ * The size of the FIFO in bytes.
+ */
+#define MCI_FIFOSIZE		(16*4)
+
+#define MCI_FIFOHALFSIZE	(MCI_FIFOSIZE / 2)
+
+#define MCI_FIFODEPTH		16
+#define MCI_HFIFO_COUNT		(MCI_FIFODEPTH / 2)
+
+#endif  /*  __QCA_MMC_H_ */