[U-Boot] [PATCH] MMC: MSHCI: Add MSHCI driver

Rajeshwari Shinde rajeshwari.s at samsung.com
Fri May 25 13:51:00 CEST 2012


Add MSHCI driver support and resgister description for same.

Signed-off-by: Alim Akhtar <alim.akhtar at samsung.com>
Signed-off-by: Terry Lambert <tlambert at chromium.org>
Signed-off-by: Rajeshwari Shinde <rajeshwari.s at samsung.com>
---
 arch/arm/include/asm/arch-exynos/mshc.h |  174 ++++++++++
 drivers/mmc/Makefile                    |    1 +
 drivers/mmc/exynos_mshc.c               |  553 +++++++++++++++++++++++++++++++
 3 files changed, 728 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-exynos/mshc.h
 create mode 100644 drivers/mmc/exynos_mshc.c

diff --git a/arch/arm/include/asm/arch-exynos/mshc.h b/arch/arm/include/asm/arch-exynos/mshc.h
new file mode 100644
index 0000000..7226619
--- /dev/null
+++ b/arch/arm/include/asm/arch-exynos/mshc.h
@@ -0,0 +1,174 @@
+/*
+ * (C) Copyright 2012 SAMSUNG Electronics
+ * Abhilash Kesavan <a.kesavan at samsung.com>
+ *
+ * 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 __ASM_ARCH_COMMON_MSHC_H
+#define __ASM_ARCH_COMMON_MSHC_H
+
+#include <asm/arch/pinmux.h>
+#ifndef __ASSEMBLY__
+struct mshci_host {
+	struct exynos_mshci	*reg;		/* Mapped address */
+	unsigned int		clock;		/* Current clock in MHz */
+	enum periph_id	peripheral;
+};
+
+struct exynos_mshci {
+	unsigned int	ctrl;
+	unsigned int	pwren;
+	unsigned int	clkdiv;
+	unsigned int	clksrc;
+	unsigned int	clkena;
+	unsigned int	tmout;
+	unsigned int	ctype;
+	unsigned int	blksiz;
+	unsigned int	bytcnt;
+	unsigned int	intmask;
+	unsigned int	cmdarg;
+	unsigned int	cmd;
+	unsigned int	resp0;
+	unsigned int	resp1;
+	unsigned int	resp2;
+	unsigned int	resp3;
+	unsigned int	mintsts;
+	unsigned int	rintsts;
+	unsigned int	status;
+	unsigned int	fifoth;
+	unsigned int	cdetect;
+	unsigned int	wrtprt;
+	unsigned int	gpio;
+	unsigned int	tcbcnt;
+	unsigned int	tbbcnt;
+	unsigned int	debnce;
+	unsigned int	usrid;
+	unsigned int	verid;
+	unsigned int	hcon;
+	unsigned int	uhs_reg;
+	unsigned int	rst_n;
+	unsigned char	reserved1[4];
+	unsigned int	bmod;
+	unsigned int	pldmnd;
+	unsigned int	dbaddr;
+	unsigned int	idsts;
+	unsigned int	idinten;
+	unsigned int	dscaddr;
+	unsigned int	bufaddr;
+	unsigned int	clksel;
+	unsigned char	reserved2[460];
+	unsigned int	cardthrctl;
+};
+
+/*
+ * Struct idma
+ * Holds the descriptor list
+ */
+struct mshci_idmac {
+	u32	des0;
+	u32	des1;
+	u32	des2;
+	u32	des3;
+};
+
+/*  Control Register  Register */
+#define CTRL_RESET	(0x1 << 0)
+#define FIFO_RESET	(0x1 << 1)
+#define DMA_RESET	(0x1 << 2)
+#define DMA_ENABLE	(0x1 << 5)
+#define SEND_AS_CCSD	(0x1 << 10)
+#define ENABLE_IDMAC    (0x1 << 25)
+
+/*  Power Enable Register */
+#define POWER_ENABLE	(0x1 << 0)
+
+/*  Clock Enable Register */
+#define CLK_ENABLE	(0x1 << 0)
+#define CLK_DISABLE	(0x0 << 0)
+
+/* Timeout Register */
+#define TMOUT_MAX	0xffffffff
+
+/*  Card Type Register */
+#define PORT0_CARD_WIDTH1	0
+#define PORT0_CARD_WIDTH4	(0x1 << 0)
+#define PORT0_CARD_WIDTH8	(0x1 << 16)
+
+/*  Interrupt Mask Register */
+#define INTMSK_ALL	0xffffffff
+#define INTMSK_RE	(0x1 << 1)
+#define INTMSK_CDONE	(0x1 << 2)
+#define INTMSK_DTO	(0x1 << 3)
+#define INTMSK_DCRC	(0x1 << 7)
+#define INTMSK_RTO	(0x1 << 8)
+#define INTMSK_DRTO	(0x1 << 9)
+#define INTMSK_HTO	(0x1 << 10)
+#define INTMSK_FRUN	(0x1 << 11)
+#define INTMSK_HLE	(0x1 << 12)
+#define INTMSK_SBE	(0x1 << 13)
+#define INTMSK_ACD	(0x1 << 14)
+#define INTMSK_EBE	(0x1 << 15)
+
+/* Command Register */
+#define CMD_RESP_EXP_BIT	(0x1 << 6)
+#define CMD_RESP_LENGTH_BIT	(0x1 << 7)
+#define CMD_CHECK_CRC_BIT	(0x1 << 8)
+#define CMD_DATA_EXP_BIT	(0x1 << 9)
+#define CMD_RW_BIT		(0x1 << 10)
+#define CMD_SENT_AUTO_STOP_BIT	(0x1 << 12)
+#define CMD_WAIT_PRV_DAT_BIT	(0x1 << 13)
+#define CMD_SEND_CLK_ONLY	(0x1 << 21)
+#define CMD_USE_HOLD_REG	(0x1 << 29)
+#define CMD_STRT_BIT		(0x1 << 31)
+#define CMD_ONLY_CLK		(CMD_STRT_BIT | CMD_SEND_CLK_ONLY | \
+				CMD_WAIT_PRV_DAT_BIT)
+
+/*  Raw Interrupt Register */
+#define DATA_ERR	(INTMSK_EBE | INTMSK_SBE | INTMSK_HLE |	\
+			INTMSK_FRUN | INTMSK_EBE | INTMSK_DCRC)
+#define DATA_TOUT	(INTMSK_HTO | INTMSK_DRTO)
+
+/*  Status Register */
+#define DATA_BUSY	(0x1 << 9)
+
+/*  FIFO Threshold Watermark Register */
+#define TX_WMARK	(0xFFF << 0)
+#define RX_WMARK	(0xFFF << 16)
+#define MSIZE_MASK	(0x7 << 28)
+
+/* DW DMA Mutiple Transaction Size */
+#define MSIZE_8		(2 << 28)
+
+/*  Bus Mode Register */
+#define BMOD_IDMAC_RESET	(0x1 << 0)
+#define BMOD_IDMAC_FB		(0x1 << 1)
+#define BMOD_IDMAC_ENABLE	(0x1 << 7)
+
+/* IDMAC bits */
+#define MSHCI_IDMAC_OWN		(0x1 << 31)
+#define MSHCI_IDMAC_CH		(0x1 << 4)
+#define MSHCI_IDMAC_FS		(0x1 << 3)
+#define MSHCI_IDMAC_LD		(0x1 << 2)
+
+#define MAX_MSHCI_CLOCK		52000000 /* Max limit is 52MHz */
+#define MIN_MSHCI_CLOCK		400000 /* Lower limit is 400KHz */
+#define COMMAND_TIMEOUT		10000
+#define TIMEOUT_MS		100
+
+int exynos_mshci_init(enum periph_id periph_id, int bus_width);
+
+#endif
+#endif
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index c245352..84e2d6a 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -27,6 +27,7 @@ LIB	:= $(obj)libmmc.o
 
 COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
 COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o
+COBJS-$(CONFIG_EXYNOS_MSHCI) += exynos_mshc.o
 COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 COBJS-$(CONFIG_FTSDC010) += ftsdc010_esdhc.o
 COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
diff --git a/drivers/mmc/exynos_mshc.c b/drivers/mmc/exynos_mshc.c
new file mode 100644
index 0000000..eb133c0
--- /dev/null
+++ b/drivers/mmc/exynos_mshc.c
@@ -0,0 +1,553 @@
+/*
+ * (C) Copyright 2012 Samsung Electronics Co. Ltd
+ *
+ * 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 <common.h>
+#include <mmc.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/mshc.h>
+#include <asm/arch/pinmux.h>
+
+/* support 4 mmc hosts */
+enum {
+	MAX_MMC_HOSTS	= 4
+};
+
+static struct mmc mshci_dev[MAX_MMC_HOSTS];
+static struct mshci_host mshci_host[MAX_MMC_HOSTS];
+static int num_devs;
+
+/* Struct to hold mshci register and bus width */
+struct mshci_config {
+	struct exynos_mshci *reg; /* registers address in physical memory */
+	int bus_width; /* bus width */
+	int removable; /* removable device? */
+	enum periph_id periph_id; /* Peripheral ID for this peripheral */
+};
+
+/**
+ * Set bits of MSHCI host control register.
+ *
+ * @param host	MSHCI host
+ * @param bits	bits to be set
+ * @return 0 on success, -1 on failure
+ */
+static int mshci_setbits(struct mshci_host *host, unsigned int bits)
+{
+	ulong start;
+
+	setbits_le32(&host->reg->ctrl, bits);
+
+	start = get_timer(0);
+	while (readl(&host->reg->ctrl) & bits) {
+		if (get_timer(start) > TIMEOUT_MS) {
+			debug("Set bits failed\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Reset MSHCI host control register.
+ *
+ * @param host	MSHCI host
+ * @return 0 on success, -1 on failure
+ */
+static int mshci_reset_all(struct mshci_host *host)
+{
+	ulong start;
+
+	/*
+	* Before we reset ciu check the DATA0 line.  If it is low and
+	* we resets the ciu then we might see some errors.
+	*/
+	start = get_timer(0);
+	while (readl(&host->reg->status) & DATA_BUSY) {
+		if (get_timer(start) > TIMEOUT_MS) {
+			debug("Controller did not release"
+				"data0 before ciu reset\n");
+			return -1;
+		}
+	}
+
+	if (mshci_setbits(host, CTRL_RESET)) {
+		debug("Fail to reset card.\n");
+		return -1;
+	}
+	if (mshci_setbits(host, FIFO_RESET)) {
+		debug("Fail to reset fifo.\n");
+		return -1;
+	}
+	if (mshci_setbits(host, DMA_RESET)) {
+		debug("Fail to reset dma.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void mshci_set_mdma_desc(u8 *desc_vir, u8 *desc_phy,
+		unsigned int des0, unsigned int des1, unsigned int des2)
+{
+	struct mshci_idmac *desc = (struct mshci_idmac *)desc_vir;
+
+	desc->des0 = des0;
+	desc->des1 = des1;
+	desc->des2 = des2;
+	desc->des3 = (unsigned int)desc_phy + sizeof(struct mshci_idmac);
+}
+
+static int mshci_prepare_data(struct mshci_host *host, struct mmc_data *data)
+{
+	unsigned int i;
+	unsigned int data_cnt;
+	unsigned int des_flag;
+	unsigned int blksz;
+	static struct mshci_idmac idmac_desc[0x10000];
+	struct mshci_idmac *pdesc_dmac;
+
+	if (mshci_setbits(host, FIFO_RESET)) {
+		debug("Fail to reset FIFO\n");
+		return -1;
+	}
+
+	pdesc_dmac = idmac_desc;
+	blksz = data->blocksize;
+	data_cnt = data->blocks;
+
+	for  (i = 0;; i++) {
+		des_flag = (MSHCI_IDMAC_OWN | MSHCI_IDMAC_CH);
+		des_flag |= (i == 0) ? MSHCI_IDMAC_FS : 0;
+		if (data_cnt <= 8) {
+			des_flag |= MSHCI_IDMAC_LD;
+			mshci_set_mdma_desc((u8 *)pdesc_dmac,
+			(u8 *)virt_to_phys(pdesc_dmac),
+			des_flag, blksz * data_cnt,
+			(unsigned int)(virt_to_phys(data->dest)) +
+			(unsigned int)(i * 0x1000));
+			break;
+		}
+		/* max transfer size is 4KB per descriptor */
+		mshci_set_mdma_desc((u8 *)pdesc_dmac,
+			(u8 *)virt_to_phys(pdesc_dmac),
+			des_flag, blksz * 8,
+			virt_to_phys(data->dest) +
+			(unsigned int)(i * 0x1000));
+
+		data_cnt -= 8;
+		pdesc_dmac++;
+	}
+
+	writel((unsigned int)virt_to_phys(idmac_desc), &host->reg->dbaddr);
+
+	/* enable the Internal DMA Controller */
+	setbits_le32(&host->reg->ctrl, ENABLE_IDMAC | DMA_ENABLE);
+	setbits_le32(&host->reg->bmod, BMOD_IDMAC_ENABLE | BMOD_IDMAC_FB);
+
+	writel(data->blocksize, &host->reg->blksiz);
+	writel(data->blocksize * data->blocks, &host->reg->bytcnt);
+
+	return 0;
+}
+
+static int mshci_set_transfer_mode(struct mshci_host *host,
+	struct mmc_data *data)
+{
+	int mode = CMD_DATA_EXP_BIT;
+
+	if (data->blocks > 1)
+		mode |= CMD_SENT_AUTO_STOP_BIT;
+	if (data->flags & MMC_DATA_WRITE)
+		mode |= CMD_RW_BIT;
+
+	return mode;
+}
+
+/*
+ * Sends a command out on the bus.
+ *
+ * @param mmc	mmc device
+ * @param cmd	mmc_cmd to be sent on bus
+ * @param data	mmc data to be sent (optional)
+ *
+ * @return	return 0 if ok, else -1
+ */
+static int exynos_mshci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
+		struct mmc_data *data)
+{
+	struct mshci_host *host = mmc->priv;
+
+	int flags = 0, i;
+	unsigned int mask;
+	ulong start;
+
+	/*
+	* We shouldn't wait for data inihibit for stop commands, even
+	* though they might use busy signaling
+	*/
+	start = get_timer(0);
+	while (readl(&host->reg->status) & DATA_BUSY) {
+		if (get_timer(start) > COMMAND_TIMEOUT) {
+			debug("timeout on data busy\n");
+			return -1;
+		}
+	}
+
+	if (readl(&host->reg->rintsts)) {
+		if ((readl(&host->reg->rintsts) &
+				(INTMSK_CDONE | INTMSK_ACD)) == 0)
+			debug("there are pending interrupts 0x%x\n",
+				readl(&host->reg->rintsts));
+	}
+	/* It clears all pending interrupts before sending a command*/
+	writel(INTMSK_ALL, &host->reg->rintsts);
+
+	if (data) {
+		if (mshci_prepare_data(host, data)) {
+			debug("fail to prepare data\n");
+			return -1;
+		}
+	}
+
+	writel(cmd->cmdarg, &host->reg->cmdarg);
+
+	if (data)
+		flags = mshci_set_transfer_mode(host, data);
+
+	if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
+		/* this is out of SD spec */
+		return -1;
+
+	if (cmd->resp_type & MMC_RSP_PRESENT) {
+		flags |= CMD_RESP_EXP_BIT;
+		if (cmd->resp_type & MMC_RSP_136)
+			flags |= CMD_RESP_LENGTH_BIT;
+	}
+
+	if (cmd->resp_type & MMC_RSP_CRC)
+		flags |= CMD_CHECK_CRC_BIT;
+	flags |= (cmd->cmdidx | CMD_STRT_BIT | CMD_USE_HOLD_REG |
+			CMD_WAIT_PRV_DAT_BIT);
+
+	mask = readl(&host->reg->cmd);
+	if (mask & CMD_STRT_BIT)
+		debug("cmd busy, current cmd: %d", cmd->cmdidx);
+
+	writel(flags, &host->reg->cmd);
+	/* wait for command complete by busy waiting. */
+	for (i = 0; i < COMMAND_TIMEOUT; i++) {
+		mask = readl(&host->reg->rintsts);
+		if (mask & INTMSK_CDONE) {
+			if (!data)
+				writel(mask, &host->reg->rintsts);
+			break;
+		}
+	}
+	/* timeout for command complete. */
+	if (COMMAND_TIMEOUT == i) {
+		debug("timeout waiting for status update\n");
+		return TIMEOUT;
+	}
+
+	if (mask & INTMSK_RTO) {
+		if (((cmd->cmdidx == 8 || cmd->cmdidx == 41 ||
+			cmd->cmdidx == 55)) == 0) {
+			debug("response timeout error: 0x%x cmd: %d\n",
+				mask, cmd->cmdidx);
+		}
+			return TIMEOUT;
+	} else if (mask & INTMSK_RE) {
+		debug("response error: 0x%x cmd: %d\n", mask, cmd->cmdidx);
+		return -1;
+	}
+	if (cmd->resp_type & MMC_RSP_PRESENT) {
+		if (cmd->resp_type & MMC_RSP_136) {
+			/* CRC is stripped so we need to do some shifting. */
+				cmd->response[0] = readl(&host->reg->resp3);
+				cmd->response[1] = readl(&host->reg->resp2);
+				cmd->response[2] = readl(&host->reg->resp1);
+				cmd->response[3] = readl(&host->reg->resp0);
+		} else {
+			cmd->response[0] = readl(&host->reg->resp0);
+			debug("\tcmd->response[0]: 0x%08x\n", cmd->response[0]);
+		}
+	}
+
+	if (data) {
+		while (!(mask & (DATA_ERR | DATA_TOUT | INTMSK_DTO)))
+			mask = readl(&host->reg->rintsts);
+		writel(mask, &host->reg->rintsts);
+		if (mask & (DATA_ERR | DATA_TOUT)) {
+			debug("error during transfer: 0x%x\n", mask);
+			/* make sure disable IDMAC and IDMAC_Interrupts */
+			writel((readl(&host->reg->ctrl) &
+			~(DMA_ENABLE | ENABLE_IDMAC)), &host->reg->ctrl);
+			/* mask all interrupt source of IDMAC */
+			writel(0, &host->reg->idinten);
+			return -1;
+		} else if (mask & INTMSK_DTO) {
+			debug("mshci dma interrupt end\n");
+		} else {
+			debug("unexpected condition 0x%x\n", mask);
+		}
+		/* make sure disable IDMAC and IDMAC_Interrupts */
+		writel((readl(&host->reg->ctrl) & ~(DMA_ENABLE | ENABLE_IDMAC)),
+				&host->reg->ctrl);
+		/* mask all interrupt source of IDMAC */
+		writel(0, &host->reg->idinten);
+	}
+
+	udelay(100);
+
+	return 0;
+}
+
+/*
+ * ON/OFF host controller clock
+ *
+ * @param host		pointer to mshci_host
+ * @param val		to enable/disable clock
+ */
+static void mshci_clock_onoff(struct mshci_host *host, int val)
+{
+
+	if (val) {
+		writel(CLK_ENABLE, &host->reg->clkena);
+		writel(0, &host->reg->cmd);
+		writel(CMD_ONLY_CLK, &host->reg->cmd);
+	} else {
+		writel(CLK_DISABLE, &host->reg->clkena);
+		writel(0, &host->reg->cmd);
+		writel(CMD_ONLY_CLK, &host->reg->cmd);
+	}
+}
+
+/*
+ * change host controller clock
+ *
+ * @param host		pointer to mshci_host
+ * @param clock		request clock
+ *
+ */
+static void mshci_change_clock(struct mshci_host *host, uint clock)
+{
+	int div;
+	u32 sclk_mshc;
+
+	if (clock == host->clock)
+		return;
+
+	/* If Input clock is higher than maximum mshc clock */
+	if (clock > MAX_MSHCI_CLOCK) {
+		debug("Input clock is too high\n");
+		clock = MAX_MSHCI_CLOCK;
+	}
+
+	/* disable the clock before changing it */
+	mshci_clock_onoff(host, CLK_DISABLE);
+
+	/* get the clock division */
+	if (host->peripheral == PERIPH_ID_SDMMC4)
+		sclk_mshc = get_mshci_clk_div(host->peripheral) / 2;
+	else
+		sclk_mshc = get_mshci_clk_div(host->peripheral) / 4;
+
+	/* CLKDIV */
+	for (div = 1 ; div <= 0xff; div++) {
+		if ((sclk_mshc / (2 * div)) <= clock) {
+			writel(div, &host->reg->clkdiv);
+			break;
+		}
+	}
+
+	writel(0, &host->reg->cmd);
+	writel(CMD_ONLY_CLK, &host->reg->cmd);
+
+	writel(readl(&host->reg->cmd) & (~CMD_SEND_CLK_ONLY),
+					&host->reg->cmd);
+
+	mshci_clock_onoff(host, CLK_ENABLE);
+	host->clock = clock;
+}
+
+/*
+ * Set ios for host controller clock
+ *
+ * This sets the card bus width and clksel
+ */
+static void exynos_mshci_set_ios(struct mmc *mmc)
+{
+	struct mshci_host *host = mmc->priv;
+
+	debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
+
+	if (mmc->clock > 0)
+		mshci_change_clock(host, mmc->clock);
+
+	if (mmc->bus_width == 8)
+		writel(PORT0_CARD_WIDTH8, &host->reg->ctype);
+	else if (mmc->bus_width == 4)
+		writel(PORT0_CARD_WIDTH4, &host->reg->ctype);
+	else
+		writel(PORT0_CARD_WIDTH1, &host->reg->ctype);
+
+	if (host->peripheral == PERIPH_ID_SDMMC0)
+		writel(0x03030001, &host->reg->clksel);
+	if (host->peripheral == PERIPH_ID_SDMMC2)
+		writel(0x03020001, &host->reg->clksel);
+	if (host->peripheral == PERIPH_ID_SDMMC4)
+		writel(0x00020001, &host->reg->clksel);
+}
+
+/*
+ * Fifo init for host controller
+ */
+static void mshci_fifo_init(struct mshci_host *host)
+{
+	int fifo_val, fifo_depth, fifo_threshold;
+
+	fifo_val = readl(&host->reg->fifoth);
+
+	fifo_depth = 0x80;
+	fifo_threshold = fifo_depth / 2;
+
+	fifo_val &= ~(RX_WMARK | TX_WMARK | MSIZE_MASK);
+	fifo_val |= (fifo_threshold | (fifo_threshold << 16) | MSIZE_8);
+	writel(fifo_val, &host->reg->fifoth);
+}
+
+
+static void mshci_init(struct mshci_host *host)
+{
+	/* power on the card */
+	writel(POWER_ENABLE, &host->reg->pwren);
+
+	mshci_reset_all(host);
+	mshci_fifo_init(host);
+
+	/* clear all pending interrupts */
+	writel(INTMSK_ALL, &host->reg->rintsts);
+
+	/* interrupts are not used, disable all */
+	writel(0, &host->reg->intmask);
+}
+
+static int exynos_mshci_initialize(struct mmc *mmc)
+{
+	struct mshci_host *host = (struct mshci_host *)mmc->priv;
+	unsigned int ier;
+
+	mshci_init(host);
+
+	/* enumerate at 400KHz */
+	mshci_change_clock(host, MIN_MSHCI_CLOCK);
+
+	/* set auto stop command */
+	ier = readl(&host->reg->ctrl);
+	ier |= SEND_AS_CCSD;
+	writel(ier, &host->reg->ctrl);
+
+	/* set 1bit card mode */
+	writel(PORT0_CARD_WIDTH1, &host->reg->ctype);
+
+	writel(0xfffff, &host->reg->debnce);
+
+	/* set bus mode register for IDMAC */
+	writel(BMOD_IDMAC_RESET, &host->reg->bmod);
+
+	writel(0x0, &host->reg->idinten);
+
+	/* set the max timeout for data and response */
+	writel(TMOUT_MAX, &host->reg->tmout);
+
+	return 0;
+}
+
+static int mshci_initialize(struct mshci_config *config)
+{
+	struct mshci_host *mmc_host;
+	struct mmc *mmc;
+
+	if (num_devs == MAX_MMC_HOSTS) {
+		debug("%s: Too many hosts\n", __func__);
+		return -1;
+	}
+
+	/* set the clock for mshci controller */
+	if (set_mshci_clk_div(config->periph_id)) {
+		debug("clock_set_mshci failed\n");
+		return -1;
+	}
+
+	mmc = &mshci_dev[num_devs];
+	mmc_host = &mshci_host[num_devs];
+
+	sprintf(mmc->name, "EXYNOS MSHCI%d", num_devs);
+	num_devs++;
+
+	mmc->priv = mmc_host;
+	mmc->send_cmd = exynos_mshci_send_command;
+	mmc->set_ios = exynos_mshci_set_ios;
+	mmc->init = exynos_mshci_initialize;
+
+	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC;
+
+	if (config->bus_width == 8)
+		mmc->host_caps |= MMC_MODE_8BIT;
+	else
+		mmc->host_caps |= MMC_MODE_4BIT;
+
+	mmc->f_min = MIN_MSHCI_CLOCK;
+	mmc->f_max = MAX_MSHCI_CLOCK;
+
+	exynos_pinmux_config(config->periph_id,
+			config->bus_width == 8 ? PINMUX_FLAG_8BIT_MODE : 0);
+
+	mmc_host->clock = 0;
+	mmc_host->reg =  config->reg;
+	mmc_host->peripheral =  config->periph_id;
+	mmc->b_max = 1;
+	mmc_register(mmc);
+	mmc->block_dev.removable = config->removable;
+	debug("exynos_mshci: periph_id=%d, width=%d, reg=%p\n",
+	      config->periph_id, config->bus_width, config->reg);
+
+	return 0;
+}
+
+int exynos_mshci_init(enum periph_id periph_id, int bus_width)
+{
+	struct mshci_config config;
+	int ret = 0;
+	config.bus_width = bus_width;
+	config.reg = (struct exynos_mshci *)samsung_get_base_mshci();
+	config.periph_id = periph_id;
+	config.removable = 1;
+	if (mshci_initialize(&config)) {
+		debug("%s: Failed to init MSHCI\n", __func__);
+		ret = -1;
+	}
+	return ret;
+}
-- 
1.7.4.4



More information about the U-Boot mailing list