Add support for the Freescale eSDHC found on 8379 and 8536 SoCs

This uses the new MMC framework

Some contributions by Dave Liu <daveliu@freescale.com>

Signed-off-by: Andy Fleming <afleming@freescale.com>
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 35aef31..6aa24f5 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -29,6 +29,7 @@
 COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
 COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
+COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 
 COBJS	:= $(COBJS-y)
 SRCS	:= $(COBJS:.o=.c)
diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
new file mode 100644
index 0000000..0ba45cd
--- /dev/null
+++ b/drivers/mmc/fsl_esdhc.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2007, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Based vaguely on the pxa mmc code:
+ * (C) Copyright 2003
+ * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <mmc.h>
+#include <part.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <fsl_esdhc.h>
+#include <asm/io.h>
+
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct fsl_esdhc {
+	uint	dsaddr;
+	uint	blkattr;
+	uint	cmdarg;
+	uint	xfertyp;
+	uint	cmdrsp0;
+	uint	cmdrsp1;
+	uint	cmdrsp2;
+	uint	cmdrsp3;
+	uint	datport;
+	uint	prsstat;
+	uint	proctl;
+	uint	sysctl;
+	uint	irqstat;
+	uint	irqstaten;
+	uint	irqsigen;
+	uint	autoc12err;
+	uint	hostcapblt;
+	uint	wml;
+	char	reserved1[8];
+	uint	fevt;
+	char	reserved2[168];
+	uint	hostver;
+	char	reserved3[780];
+	uint	scr;
+};
+
+/* Return the XFERTYP flags for a given command and data packet */
+uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
+{
+	uint xfertyp = 0;
+
+	if (data) {
+		xfertyp |= XFERTYP_DPSEL | XFERTYP_DMAEN;
+
+		if (data->blocks > 1) {
+			xfertyp |= XFERTYP_MSBSEL;
+			xfertyp |= XFERTYP_BCEN;
+		}
+
+		if (data->flags & MMC_DATA_READ)
+			xfertyp |= XFERTYP_DTDSEL;
+	}
+
+	if (cmd->resp_type & MMC_RSP_CRC)
+		xfertyp |= XFERTYP_CCCEN;
+	if (cmd->resp_type & MMC_RSP_OPCODE)
+		xfertyp |= XFERTYP_CICEN;
+	if (cmd->resp_type & MMC_RSP_136)
+		xfertyp |= XFERTYP_RSPTYP_136;
+	else if (cmd->resp_type & MMC_RSP_BUSY)
+		xfertyp |= XFERTYP_RSPTYP_48_BUSY;
+	else if (cmd->resp_type & MMC_RSP_PRESENT)
+		xfertyp |= XFERTYP_RSPTYP_48;
+
+	return XFERTYP_CMD(cmd->cmdidx) | xfertyp;
+}
+
+static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
+{
+	uint wml_value;
+	int timeout;
+	struct fsl_esdhc *regs = mmc->priv;
+
+	wml_value = data->blocksize/4;
+
+	if (data->flags & MMC_DATA_READ) {
+		if (wml_value > 0x10)
+			wml_value = 0x10;
+
+		wml_value = 0x100000 | wml_value;
+
+		out_be32(&regs->dsaddr, (u32)data->dest);
+	} else {
+		if (wml_value > 0x80)
+			wml_value = 0x80;
+		if ((in_be32(&regs->prsstat) & PRSSTAT_WPSPL) == 0) {
+			printf("\nThe SD card is locked. Can not write to a locked card.\n\n");
+			return TIMEOUT;
+		}
+		wml_value = wml_value << 16 | 0x10;
+		out_be32(&regs->dsaddr, (u32)data->src);
+	}
+
+	out_be32(&regs->wml, wml_value);
+
+	out_be32(&regs->blkattr, data->blocks << 16 | data->blocksize);
+
+	/* Calculate the timeout period for data transactions */
+	timeout = __ilog2(mmc->tran_speed/10);
+	timeout -= 13;
+
+	if (timeout > 14)
+		timeout = 14;
+
+	if (timeout < 0)
+		timeout = 0;
+
+	clrsetbits_be32(&regs->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16);
+
+	return 0;
+}
+
+
+/*
+ * Sends a command out on the bus.  Takes the mmc pointer,
+ * a command pointer, and an optional data pointer.
+ */
+static int
+esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
+{
+	uint	xfertyp;
+	uint	irqstat;
+	volatile struct fsl_esdhc *regs = mmc->priv;
+
+	out_be32(&regs->irqstat, -1);
+
+	sync();
+
+	/* Wait for the bus to be idle */
+	while ((in_be32(&regs->prsstat) & PRSSTAT_CICHB) ||
+			(in_be32(&regs->prsstat) & PRSSTAT_CIDHB));
+
+	while (in_be32(&regs->prsstat) & PRSSTAT_DLA);
+
+	/* Wait at least 8 SD clock cycles before the next command */
+	/*
+	 * Note: This is way more than 8 cycles, but 1ms seems to
+	 * resolve timing issues with some cards
+	 */
+	udelay(1000);
+
+	/* Set up for a data transfer if we have one */
+	if (data) {
+		int err;
+
+		err = esdhc_setup_data(mmc, data);
+		if(err)
+			return err;
+	}
+
+	/* Figure out the transfer arguments */
+	xfertyp = esdhc_xfertyp(cmd, data);
+
+	/* Send the command */
+	out_be32(&regs->cmdarg, cmd->cmdarg);
+	out_be32(&regs->xfertyp, xfertyp);
+
+	/* Wait for the command to complete */
+	while (!(in_be32(&regs->irqstat) & IRQSTAT_CC));
+
+	irqstat = in_be32(&regs->irqstat);
+	out_be32(&regs->irqstat, irqstat);
+
+	if (irqstat & CMD_ERR)
+		return COMM_ERR;
+
+	if (irqstat & IRQSTAT_CTOE)
+		return TIMEOUT;
+
+	/* Copy the response to the response buffer */
+	if (cmd->resp_type & MMC_RSP_136) {
+		u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0;
+
+		cmdrsp3 = in_be32(&regs->cmdrsp3);
+		cmdrsp2 = in_be32(&regs->cmdrsp2);
+		cmdrsp1 = in_be32(&regs->cmdrsp1);
+		cmdrsp0 = in_be32(&regs->cmdrsp0);
+		((uint *)(cmd->response))[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24);
+		((uint *)(cmd->response))[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24);
+		((uint *)(cmd->response))[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24);
+		((uint *)(cmd->response))[3] = (cmdrsp0 << 8);
+	} else
+		((uint *)(cmd->response))[0] = in_be32(&regs->cmdrsp0);
+
+	/* Wait until all of the blocks are transferred */
+	if (data) {
+		do {
+			irqstat = in_be32(&regs->irqstat);
+
+			if (irqstat & DATA_ERR)
+				return COMM_ERR;
+
+			if (irqstat & IRQSTAT_DTOE)
+				return TIMEOUT;
+		} while (!(irqstat & IRQSTAT_TC) &&
+				(in_be32(&regs->prsstat) & PRSSTAT_DLA));
+	}
+
+	out_be32(&regs->irqstat, -1);
+
+	return 0;
+}
+
+void set_sysctl(struct mmc *mmc, uint clock)
+{
+	int sdhc_clk = gd->sdhc_clk;
+	int div, pre_div;
+	volatile struct fsl_esdhc *regs = mmc->priv;
+	uint clk;
+
+	if (sdhc_clk / 16 > clock) {
+		for (pre_div = 2; pre_div < 256; pre_div *= 2)
+			if ((sdhc_clk / pre_div) <= (clock * 16))
+				break;
+	} else
+		pre_div = 2;
+
+	for (div = 1; div <= 16; div++)
+		if ((sdhc_clk / (div * pre_div)) <= clock)
+			break;
+
+	pre_div >>= 1;
+	div -= 1;
+
+	clk = (pre_div << 8) | (div << 4);
+
+	clrsetbits_be32(&regs->sysctl, SYSCTL_CLOCK_MASK, clk);
+
+	udelay(10000);
+
+	setbits_be32(&regs->sysctl, SYSCTL_PEREN);
+}
+
+static void esdhc_set_ios(struct mmc *mmc)
+{
+	struct fsl_esdhc *regs = mmc->priv;
+
+	/* Set the clock speed */
+	set_sysctl(mmc, mmc->clock);
+
+	/* Set the bus width */
+	clrbits_be32(&regs->proctl, PROCTL_DTW_4 | PROCTL_DTW_8);
+
+	if (mmc->bus_width == 4)
+		setbits_be32(&regs->proctl, PROCTL_DTW_4);
+	else if (mmc->bus_width == 8)
+		setbits_be32(&regs->proctl, PROCTL_DTW_8);
+}
+
+static int esdhc_init(struct mmc *mmc)
+{
+	struct fsl_esdhc *regs = mmc->priv;
+	int timeout = 1000;
+
+	/* Enable cache snooping */
+	out_be32(&regs->scr, 0x00000040);
+
+	out_be32(&regs->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN);
+
+	/* Set the initial clock speed */
+	set_sysctl(mmc, 400000);
+
+	/* Disable the BRR and BWR bits in IRQSTAT */
+	clrbits_be32(&regs->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR);
+
+	/* Put the PROCTL reg back to the default */
+	out_be32(&regs->proctl, PROCTL_INIT);
+
+	while (!(in_be32(&regs->prsstat) & PRSSTAT_CINS) && --timeout)
+		udelay(1000);
+
+	if (timeout <= 0)
+		return NO_CARD_ERR;
+
+	return 0;
+}
+
+static int esdhc_initialize(bd_t *bis)
+{
+	struct fsl_esdhc *regs = (struct fsl_esdhc *)CONFIG_SYS_FSL_ESDHC_ADDR;
+	struct mmc *mmc;
+	u32 caps;
+
+	mmc = malloc(sizeof(struct mmc));
+
+	sprintf(mmc->name, "FSL_ESDHC");
+	mmc->priv = regs;
+	mmc->send_cmd = esdhc_send_cmd;
+	mmc->set_ios = esdhc_set_ios;
+	mmc->init = esdhc_init;
+
+	caps = regs->hostcapblt;
+
+	if (caps & ESDHC_HOSTCAPBLT_VS18)
+		mmc->voltages |= MMC_VDD_165_195;
+	if (caps & ESDHC_HOSTCAPBLT_VS30)
+		mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
+	if (caps & ESDHC_HOSTCAPBLT_VS33)
+		mmc->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
+
+	if (caps & ESDHC_HOSTCAPBLT_HSS)
+		mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+
+	mmc->f_min = 400000;
+	mmc->f_max = MIN(gd->sdhc_clk, 50000000);
+
+	mmc_register(mmc);
+
+	return 0;
+}
+
+int fsl_esdhc_mmc_init(bd_t *bis)
+{
+	return esdhc_initialize(bis);
+}
diff --git a/include/fsl_esdhc.h b/include/fsl_esdhc.h
new file mode 100644
index 0000000..0a5c5d6
--- /dev/null
+++ b/include/fsl_esdhc.h
@@ -0,0 +1,145 @@
+/*
+ * FSL SD/MMC Defines
+ *-------------------------------------------------------------------
+ *
+ * Copyright 2007-2008, Freescale Semiconductor, Inc
+ *
+ * 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, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ *-------------------------------------------------------------------
+ *
+ */
+
+#ifndef  __FSL_ESDHC_H__
+#define	__FSL_ESDHC_H__
+
+/* FSL eSDHC-specific constants */
+#define SYSCTL			0x0002e02c
+#define SYSCTL_INITA		0x08000000
+#define SYSCTL_TIMEOUT_MASK	0x000f0000
+#define SYSCTL_CLOCK_MASK	0x00000fff
+#define SYSCTL_PEREN		0x00000004
+#define SYSCTL_HCKEN		0x00000002
+#define SYSCTL_IPGEN		0x00000001
+
+#define IRQSTAT			0x0002e030
+#define IRQSTAT_DMAE		(0x10000000)
+#define IRQSTAT_AC12E		(0x01000000)
+#define IRQSTAT_DEBE		(0x00400000)
+#define IRQSTAT_DCE		(0x00200000)
+#define IRQSTAT_DTOE		(0x00100000)
+#define IRQSTAT_CIE		(0x00080000)
+#define IRQSTAT_CEBE		(0x00040000)
+#define IRQSTAT_CCE		(0x00020000)
+#define IRQSTAT_CTOE		(0x00010000)
+#define IRQSTAT_CINT		(0x00000100)
+#define IRQSTAT_CRM		(0x00000080)
+#define IRQSTAT_CINS		(0x00000040)
+#define IRQSTAT_BRR		(0x00000020)
+#define IRQSTAT_BWR		(0x00000010)
+#define IRQSTAT_DINT		(0x00000008)
+#define IRQSTAT_BGE		(0x00000004)
+#define IRQSTAT_TC		(0x00000002)
+#define IRQSTAT_CC		(0x00000001)
+
+#define CMD_ERR		(IRQSTAT_CIE | IRQSTAT_CEBE | IRQSTAT_CCE)
+#define DATA_ERR	(IRQSTAT_DEBE | IRQSTAT_DCE | IRQSTAT_DTOE)
+
+#define IRQSTATEN		0x0002e034
+#define IRQSTATEN_DMAE		(0x10000000)
+#define IRQSTATEN_AC12E		(0x01000000)
+#define IRQSTATEN_DEBE		(0x00400000)
+#define IRQSTATEN_DCE		(0x00200000)
+#define IRQSTATEN_DTOE		(0x00100000)
+#define IRQSTATEN_CIE		(0x00080000)
+#define IRQSTATEN_CEBE		(0x00040000)
+#define IRQSTATEN_CCE		(0x00020000)
+#define IRQSTATEN_CTOE		(0x00010000)
+#define IRQSTATEN_CINT		(0x00000100)
+#define IRQSTATEN_CRM		(0x00000080)
+#define IRQSTATEN_CINS		(0x00000040)
+#define IRQSTATEN_BRR		(0x00000020)
+#define IRQSTATEN_BWR		(0x00000010)
+#define IRQSTATEN_DINT		(0x00000008)
+#define IRQSTATEN_BGE		(0x00000004)
+#define IRQSTATEN_TC		(0x00000002)
+#define IRQSTATEN_CC		(0x00000001)
+
+#define PRSSTAT			0x0002e024
+#define PRSSTAT_CLSL		(0x00800000)
+#define PRSSTAT_WPSPL		(0x00080000)
+#define PRSSTAT_CDPL		(0x00040000)
+#define PRSSTAT_CINS		(0x00010000)
+#define PRSSTAT_BREN		(0x00000800)
+#define PRSSTAT_DLA		(0x00000004)
+#define PRSSTAT_CICHB		(0x00000002)
+#define PRSSTAT_CIDHB		(0x00000001)
+
+#define PROCTL			0x0002e028
+#define PROCTL_INIT		0x00000020
+#define PROCTL_DTW_4		0x00000002
+#define PROCTL_DTW_8		0x00000004
+
+#define CMDARG			0x0002e008
+
+#define XFERTYP			0x0002e00c
+#define XFERTYP_CMD(x)		((x & 0x3f) << 24)
+#define XFERTYP_CMDTYP_NORMAL	0x0
+#define XFERTYP_CMDTYP_SUSPEND	0x00400000
+#define XFERTYP_CMDTYP_RESUME	0x00800000
+#define XFERTYP_CMDTYP_ABORT	0x00c00000
+#define XFERTYP_DPSEL		0x00200000
+#define XFERTYP_CICEN		0x00100000
+#define XFERTYP_CCCEN		0x00080000
+#define XFERTYP_RSPTYP_NONE	0
+#define XFERTYP_RSPTYP_136	0x00010000
+#define XFERTYP_RSPTYP_48	0x00020000
+#define XFERTYP_RSPTYP_48_BUSY	0x00030000
+#define XFERTYP_MSBSEL		0x00000020
+#define XFERTYP_DTDSEL		0x00000010
+#define XFERTYP_AC12EN		0x00000004
+#define XFERTYP_BCEN		0x00000002
+#define XFERTYP_DMAEN		0x00000001
+
+#define CINS_TIMEOUT		1000
+
+#define DSADDR		0x2e004
+
+#define CMDRSP0		0x2e010
+#define CMDRSP1		0x2e014
+#define CMDRSP2		0x2e018
+#define CMDRSP3		0x2e01c
+
+#define DATPORT		0x2e020
+
+#define WML		0x2e044
+#define WML_WRITE	0x00010000
+
+#define BLKATTR		0x2e004
+#define BLKATTR_CNT(x)	((x & 0xffff) << 16)
+#define BLKATTR_SIZE(x)	(x & 0x1fff)
+#define MAX_BLK_CNT	0x7fff	/* so malloc will have enough room with 32M */
+
+#define ESDHC_HOSTCAPBLT_VS18	0x04000000
+#define ESDHC_HOSTCAPBLT_VS30	0x02000000
+#define ESDHC_HOSTCAPBLT_VS33	0x01000000
+#define ESDHC_HOSTCAPBLT_SRS	0x00800000
+#define ESDHC_HOSTCAPBLT_DMAS	0x00400000
+#define ESDHC_HOSTCAPBLT_HSS	0x00200000
+
+int fsl_esdhc_mmc_init(bd_t *bis);
+
+#endif  /* __FSL_ESDHC_H__ */