[U-Boot] [PATCH v3] AT91: MCI: add SD/MMC driver using mmc framework

Reinhard Meyer u-boot at emk-elektronik.de
Tue Aug 17 16:04:48 CEST 2010


This patch adds SD/MMC support to AT91SAM9260/9XE/9G20
using the generic MMC framework.

Signed-off-by: Reinhard Meyer <u-boot at emk-elektronik.de>
---
The driver itself is not dependent on any particular AT91 or AVR32
device, but further work in device specific files is
required to enable its use for other AT91 (and AVR32) devices:
at91<*>_devices.c, hardware.h, at91<*>.h.
Patches welcome!

 arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c |   21 ++
 arch/arm/include/asm/arch-at91/at91_common.h      |    1 +
 arch/arm/include/asm/arch-at91/clk.h              |    5 +
 arch/arm/include/asm/arch-at91/hardware.h         |    1 +
 doc/README.atmel_mci                              |   86 +++++
 drivers/mmc/Makefile                              |    5 +-
 drivers/mmc/atmel_mci.h                           |   48 +++-
 drivers/mmc/gen_atmel_mci.c                       |  353 +++++++++++++++++++++
 include/mmc.h                                     |   15 +-
 9 files changed, 530 insertions(+), 5 deletions(-)
 create mode 100644 doc/README.atmel_mci
 create mode 100644 drivers/mmc/gen_atmel_mci.c

diff --git a/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c b/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
index 77d49ab..2d88481 100644
--- a/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
+++ b/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
@@ -194,3 +194,24 @@ void at91_macb_hw_init(void)
 #endif
 }
 #endif
+
+#if defined(CONFIG_ATMEL_MCI) || defined(CONFIG_GENERIC_ATMEL_MCI)
+void at91_mci_hw_init(void)
+{
+	at91_set_a_periph(AT91_PIO_PORTA, 8, 1);	/* MCCK */
+#if defined(CONFIG_ATMEL_MCI_PORTB)
+	at91_set_b_periph(AT91_PIO_PORTA, 1, 1);	/* MCCDB */
+	at91_set_b_periph(AT91_PIO_PORTA, 0, 1);	/* MCDB0 */
+	at91_set_b_periph(AT91_PIO_PORTA, 5, 1);	/* MCDB1 */
+	at91_set_b_periph(AT91_PIO_PORTA, 4, 1);	/* MCDB2 */
+	at91_set_b_periph(AT91_PIO_PORTA, 3, 1);	/* MCDB3 */
+#else
+	at91_set_a_periph(AT91_PIO_PORTA, 7, 1);	/* MCCDA */
+	at91_set_a_periph(AT91_PIO_PORTA, 6, 1);	/* MCDA0 */
+	at91_set_a_periph(AT91_PIO_PORTA, 9, 1);	/* MCDA1 */
+	at91_set_a_periph(AT91_PIO_PORTA, 10, 1);	/* MCDA2 */
+	at91_set_a_periph(AT91_PIO_PORTA, 11, 1);	/* MCDA3 */
+#endif
+}
+#endif
+
diff --git a/arch/arm/include/asm/arch-at91/at91_common.h b/arch/arm/include/asm/arch-at91/at91_common.h
index 01840ee..0067190 100644
--- a/arch/arm/include/asm/arch-at91/at91_common.h
+++ b/arch/arm/include/asm/arch-at91/at91_common.h
@@ -27,6 +27,7 @@
 
 void at91_can_hw_init(void);
 void at91_macb_hw_init(void);
+void at91_mci_hw_init(void);
 void at91_serial_hw_init(void);
 void at91_serial0_hw_init(void);
 void at91_serial1_hw_init(void);
diff --git a/arch/arm/include/asm/arch-at91/clk.h b/arch/arm/include/asm/arch-at91/clk.h
index f642dd9..457e6c9 100644
--- a/arch/arm/include/asm/arch-at91/clk.h
+++ b/arch/arm/include/asm/arch-at91/clk.h
@@ -59,5 +59,10 @@ static inline unsigned long get_twi_clk_rate(unsigned int dev_id)
 	return get_mck_clk_rate();
 }
 
+static inline unsigned long get_mci_clk_rate(void)
+{
+	return get_mck_clk_rate();
+}
+
 int at91_clock_init(unsigned long main_clock);
 #endif /* __ASM_ARM_ARCH_CLK_H__ */
diff --git a/arch/arm/include/asm/arch-at91/hardware.h b/arch/arm/include/asm/arch-at91/hardware.h
index 4ddb315..9f732a7 100644
--- a/arch/arm/include/asm/arch-at91/hardware.h
+++ b/arch/arm/include/asm/arch-at91/hardware.h
@@ -20,6 +20,7 @@
 #include <asm/arch-at91/at91rm9200.h>
 #elif defined(CONFIG_AT91SAM9260) || defined(CONFIG_AT91SAM9G20)
 #include <asm/arch/at91sam9260.h>
+#define AT91_BASE_MCI	AT91SAM9260_BASE_MCI
 #define AT91_BASE_SPI	AT91SAM9260_BASE_SPI0
 #define AT91_ID_UHP	AT91SAM9260_ID_UHP
 #define AT91_PMC_UHP	AT91SAM926x_PMC_UHP
diff --git a/doc/README.atmel_mci b/doc/README.atmel_mci
new file mode 100644
index 0000000..18b1bdf
--- /dev/null
+++ b/doc/README.atmel_mci
@@ -0,0 +1,86 @@
+How to use SD/MMC cards with Atmel SoCs having MCI hardware
+-----------------------------------------------------------
+2010-08-16 Reinhard Meyer <reinhard.meyer at emk-elektronik.de>
+
+This is a new approach to use Atmel MCI hardware with the
+general MMC framework. Therefore it benefits from that
+framework's abilities to handle SDHC Cards and the ability
+to write blocks.
+
+- AT91SAM9XE512 (tested, will definitely work with XE128 and XE256)
+- AT91SAM9260 (not tested, but MCI is to AT91SAM9XE)
+- AT91SAM9G20 (not tested, should work)
+
+It should work with all other ATMEL devices that have MCI,
+including AVR32.
+
+The generic driver does NOT assign port pins to the MCI block
+nor does it start the MCI clock. This has to be handled in a
+board/SoC specific manner before the driver is initialized:
+
+example: this is added to at91sam9260_devices.c:
+
+#if defined(CONFIG_ATMEL_MCI) || defined(CONFIG_GENERIC_ATMEL_MCI)
+void at91_mci_hw_init(void)
+{
+	at91_set_a_periph(AT91_PIO_PORTA, 8, PUP);	/* MCCK */
+#if defined(CONFIG_ATMEL_MCI_PORTB)
+	at91_set_b_periph(AT91_PIO_PORTA, 1, PUP);	/* MCCDB */
+	at91_set_b_periph(AT91_PIO_PORTA, 0, PUP);	/* MCDB0 */
+	at91_set_b_periph(AT91_PIO_PORTA, 5, PUP);	/* MCDB1 */
+	at91_set_b_periph(AT91_PIO_PORTA, 4, PUP);	/* MCDB2 */
+	at91_set_b_periph(AT91_PIO_PORTA, 3, PUP);	/* MCDB3 */
+#else
+	at91_set_a_periph(AT91_PIO_PORTA, 7, PUP);	/* MCCDA */
+	at91_set_a_periph(AT91_PIO_PORTA, 6, PUP);	/* MCDA0 */
+	at91_set_a_periph(AT91_PIO_PORTA, 9, PUP);	/* MCDA1 */
+	at91_set_a_periph(AT91_PIO_PORTA, 10, PUP);	/* MCDA2 */
+	at91_set_a_periph(AT91_PIO_PORTA, 11, PUP);	/* MCDA3 */
+#endif
+}
+#endif
+
+the board specific file need added:
+...
+#ifdef CONFIG_GENERIC_ATMEL_MCI
+# include <mmc.h>
+#endif
+...
+#ifdef CONFIG_GENERIC_ATMEL_MCI
+/* this is a weak define that we are overriding */
+int board_mmc_init(bd_t *bd)
+{
+	/* Enable clock */
+	at91_sys_write(AT91_PMC_PCER, 1 << AT91SAM9260_ID_MCI);
+	at91_mci_hw_init();
+
+	/* This calls the atmel_mci_init in gen_atmel_mci.c */
+	return atmel_mci_init((void *)AT91_BASE_MCI);
+}
+
+/* this is a weak define that we are overriding */
+int board_mmc_getcd(u8 *cd, struct mmc *mmc)
+{
+	/*
+	 * the only currently existing use of this function
+	 * (fsl_esdhc.c) suggests this function must return
+	 * *cs = TRUE if a card is NOT detected -> in most
+	 * cases the value of the pin when the detect switch
+	 * closes to GND
+	 */
+	*cd = at91_get_gpio_value (CONFIG_SYS_MMC_CD_PIN) ? 1 : 0;
+	return 0;
+}
+
+#endif
+
+and the board definition files needs:
+
+/* SD/MMC card */
+#define CONFIG_MMC			1
+#define CONFIG_GENERIC_MMC		1
+#define CONFIG_GENERIC_ATMEL_MCI	1
+#define CONFIG_ATMEL_MCI_PORTB		1	/* Atmel XE-EK uses port B */
+#define CONFIG_SYS_MMC_CD_PIN		AT91_PIN_PC9
+#define CONFIG_CMD_MMC			1
+
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6fa04b8..3180776 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -25,12 +25,13 @@ include $(TOPDIR)/config.mk
 
 LIB	:= $(obj)libmmc.a
 
-COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 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-$(CONFIG_GENERIC_MMC) += mmc.o
+COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o
 COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
+COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
 COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o
 
 COBJS	:= $(COBJS-y)
diff --git a/drivers/mmc/atmel_mci.h b/drivers/mmc/atmel_mci.h
index 5b4f5c9..823a77d 100644
--- a/drivers/mmc/atmel_mci.h
+++ b/drivers/mmc/atmel_mci.h
@@ -22,7 +22,45 @@
 #ifndef __CPU_AT32AP_ATMEL_MCI_H__
 #define __CPU_AT32AP_ATMEL_MCI_H__
 
-/* Atmel MultiMedia Card Interface (MCI) registers */
+#ifndef __ASSEMBLY__
+
+/*
+ * Structure for struct SoC access.
+ * Names starting with '_' are fillers.
+ */
+typedef struct atmel_mci {
+	/*	reg	Offset */
+	u32	cr;	/* 0x00 */
+	u32	mr;	/* 0x04 */
+	u32	dtor;	/* 0x08 */
+	u32	sdcr;	/* 0x0c */
+	u32	argr;	/* 0x10 */
+	u32	cmdr;	/* 0x14 */
+	u32	_18;	/* 0x18 */
+	u32	_1c;	/* 0x1c */
+	u32	rspr;	/* 0x20 */
+	u32	rspr1;	/* 0x24 */
+	u32	rspr2;	/* 0x28 */
+	u32	rspr3;	/* 0x2c */
+	u32	rdr;	/* 0x30 */
+	u32	tdr;	/* 0x34 */
+	u32	_38;	/* 0x38 */
+	u32	_3c;	/* 0x3c */
+	u32	sr;	/* 0x40 */
+	u32	ier;	/* 0x44 */
+	u32	idr;	/* 0x48 */
+	u32	imr;	/* 0x4c */
+} atmel_mci_t;
+
+#endif /* __ASSEMBLY__ */
+
+/*
+ * NOTICE: Use of registers offsets is depreciated.
+ * These defines will be removed once the old driver
+ * is taken out of commision.
+ *
+ * Atmel MultiMedia Card Interface (MCI) registers
+ */
 #define MMCI_CR					0x0000
 #define MMCI_MR					0x0004
 #define MMCI_DTOR				0x0008
@@ -192,7 +230,13 @@
 		    << MMCI_##name##_OFFSET))		\
 	 | MMCI_BF(name,value))
 
-/* Register access macros */
+/*
+ * NOTICE: Use of registers offsets is depreciated.
+ * These defines will be removed once the old driver
+ * is taken out of commision.
+ *
+ * Register access macros
+ */
 #define mmci_readl(reg)					\
 	readl((void *)MMCI_BASE + MMCI_##reg)
 #define mmci_writel(reg,value)				\
diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c
new file mode 100644
index 0000000..6754efa
--- /dev/null
+++ b/drivers/mmc/gen_atmel_mci.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2010
+ * Rob Emanuele <rob at emanuele.us>
+ * Reinhard Meyer, EMK Elektronik <reinhard.meyer at emk-elektronik.de>
+ *
+ * Original Driver:
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * 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 <part.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/errno.h>
+#include <asm/byteorder.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/memory-map.h>
+#include "atmel_mci.h"
+
+#ifndef CONFIG_SYS_MMC_CLK_OD
+# define CONFIG_SYS_MMC_CLK_OD	150000
+#endif
+
+#define MMC_DEFAULT_BLKLEN	512
+
+#if defined(CONFIG_ATMEL_MCI_PORTB)
+# define MCI_BUS 1
+#else
+# define MCI_BUS 0
+#endif
+
+static int initialized = 0;
+
+/*
+ * print command and status:
+ *
+ * always when DEBUG is defined
+ * on command errors
+ */
+static void dump_cmd(u32 cmdr, u32 arg, u32 status, const char* msg)
+{
+	printf("gen_atmel_mci: CMDR %08x (%2u) ARGR %08x (SR: %08x) %s\n",
+		cmdr, cmdr&0x3F, arg, status, msg);
+}
+
+/* Setup for MCI Clock and Block Size */
+static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen)
+{
+	atmel_mci_t *mci = (atmel_mci_t *)mmc->priv;
+	u32 bus_hz = get_mci_clk_rate();
+	u32 clkdiv = 255;
+
+	debug("mci: bus_hz is %u, setting clock %u Hz, block size %u\n",
+		bus_hz, hz, blklen);
+	if (hz > 0) {
+		/* find lowest clkdiv yielding a rate <= than requested */
+		for (clkdiv=0; clkdiv<255; clkdiv++) {
+			if ((bus_hz / (clkdiv+1) / 2) <= hz)
+				break;
+		}
+	}
+	printf("mci: setting clock %u Hz, block size %u\n",
+		(bus_hz / (clkdiv+1)) / 2, blklen);
+
+	blklen &= 0xfffc;
+	/* On some platforms RDPROOF and WRPROOF are ignored */
+	writel((MMCI_BF(CLKDIV, clkdiv)
+		 | MMCI_BF(BLKLEN, blklen)
+		 | MMCI_BIT(RDPROOF)
+		 | MMCI_BIT(WRPROOF)), &mci->mr);
+	initialized = 1;
+}
+
+/* Return the CMDR with flags for a given command and data packet */
+static u32 mci_encode_cmd(
+	struct mmc_cmd *cmd, struct mmc_data *data, u32* error_flags)
+{
+	u32 cmdr = 0;
+
+	/* Default Flags for Errors */
+	*error_flags |= (MMCI_BIT(DTOE) | MMCI_BIT(RDIRE) | MMCI_BIT(RENDE) |
+		MMCI_BIT(RINDE) | MMCI_BIT(RTOE));
+
+	/* Default Flags for the Command */
+	cmdr |= MMCI_BIT(MAXLAT);
+
+	if (data) {
+		cmdr |= MMCI_BF(TRCMD, 1);
+		if (data->blocks > 1)
+			cmdr |= MMCI_BF(TRTYP, 1);
+		if (data->flags & MMC_DATA_READ)
+			cmdr |= MMCI_BIT(TRDIR);
+	}
+
+	if (cmd->resp_type & MMC_RSP_CRC)
+		*error_flags |= MMCI_BIT(RCRCE);
+	if (cmd->resp_type & MMC_RSP_136)
+		cmdr |= MMCI_BF(RSPTYP, 2);
+	else if (cmd->resp_type & MMC_RSP_BUSY)
+		cmdr |= MMCI_BF(RSPTYP, 3);
+	else if (cmd->resp_type & MMC_RSP_PRESENT)
+		cmdr |= MMCI_BF(RSPTYP, 1);
+
+	return cmdr | MMCI_BF(CMDNB, cmd->cmdidx);
+}
+
+/* entered into function pointer in mci_send_cmd */
+static u32 mci_data_read(atmel_mci_t *mci, u32* data, u32 error_flags)
+{
+	u32 status;
+
+	do {
+		status = readl(&mci->sr);
+		if (status & (error_flags | MMCI_BIT(OVRE)))
+			goto io_fail;
+	} while (!(status & MMCI_BIT(RXRDY)));
+
+	if (status & MMCI_BIT(RXRDY)) {
+		*data = readl(&mci->rdr);
+		status = 0;
+	}
+io_fail:
+	return status;
+}
+
+/* entered into function pointer in mci_send_cmd */
+static u32 mci_data_write(atmel_mci_t *mci, u32* data, u32 error_flags)
+{
+	u32 status;
+
+	do {
+		status = readl(&mci->sr);
+		if (status & (error_flags | MMCI_BIT(UNRE)))
+			goto io_fail;
+	} while (!(status & MMCI_BIT(TXRDY)));
+
+	if (status & MMCI_BIT(TXRDY)) {
+		writel(*data, &mci->tdr);
+		status = 0;
+	}
+io_fail:
+	return status;
+}
+
+/*
+ * entered into mmc structure during driver init
+ *
+ * Sends a command out on the bus and deals with the block data.
+ * Takes the mmc pointer, a command pointer, and an optional data pointer.
+ */
+static int
+mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
+{
+	atmel_mci_t *mci = (atmel_mci_t *)mmc->priv;
+	u32 cmdr;
+	u32 error_flags = 0;
+	u32 status;
+
+	if (!initialized) {
+		puts ("MCI not initialized!\n");
+		return COMM_ERR;
+	}
+
+	/* Figure out the transfer arguments */
+	cmdr = mci_encode_cmd(cmd, data, &error_flags);
+
+	/* Send the command */
+	writel(cmd->cmdarg, &mci->argr);
+	writel(cmdr, &mci->cmdr);
+
+#ifdef DEBUG
+	dump_cmd(cmdr, cmd->cmdarg, 0, "DEBUG");
+#endif
+
+	/* Wait for the command to complete */
+	while (!((status = readl(&mci->sr)) & MMCI_BIT(CMDRDY)));
+
+	if (status & error_flags) {
+		dump_cmd(cmdr, cmd->cmdarg, status, "Command Failed");
+		return COMM_ERR;
+	}
+
+	/* Copy the response to the response buffer */
+	if (cmd->resp_type & MMC_RSP_136) {
+		cmd->response[0] = readl(&mci->rspr);
+		cmd->response[1] = readl(&mci->rspr1);
+		cmd->response[2] = readl(&mci->rspr2);
+		cmd->response[3] = readl(&mci->rspr3);
+	} else
+		cmd->response[0] = readl(&mci->rspr);
+
+	/* transfer all of the blocks */
+	if (data) {
+		u32 word_count, block_count;
+		u32* ioptr;
+		u32 sys_blocksize, dummy, i;
+		u32 (*mci_data_op)
+			(atmel_mci_t *mci, u32* data, u32 error_flags);
+
+		if (data->flags & MMC_DATA_READ) {
+			mci_data_op = mci_data_read;
+			sys_blocksize = mmc->read_bl_len;
+			ioptr = (u32*)data->dest;
+		} else {
+			mci_data_op = mci_data_write;
+			sys_blocksize = mmc->write_bl_len;
+			ioptr = (u32*)data->src;
+		}
+
+		status = 0;
+		for (block_count = 0;
+				block_count < data->blocks && !status;
+				block_count++) {
+			word_count = 0;
+			do {
+				status = mci_data_op(mci, ioptr, error_flags);
+				word_count++;
+				ioptr++;
+			} while (!status && word_count < (data->blocksize/4));
+#ifdef DEBUG
+			if (data->flags & MMC_DATA_READ)
+			{
+				printf("Read Data:\n");
+				print_buffer(0, data->dest, 1,
+					word_count*4, 0);
+			}
+#endif
+#ifdef DEBUG
+			if (!status && word_count < (sys_blocksize / 4))
+				printf("filling rest of block...\n");
+#endif
+			/* fill the rest of a full block */
+			while (!status && word_count < (sys_blocksize / 4)) {
+				status = mci_data_op(mci, &dummy,
+					error_flags);
+				word_count++;
+			}
+			if (status) {
+				dump_cmd(cmdr, cmd->cmdarg, status,
+					"Data Transfer Failed");
+				return COMM_ERR;
+			}
+		}
+
+		/* Wait for Transfer End */
+		i = 0;
+		do {
+			status = readl(&mci->sr);
+
+			if (status & error_flags) {
+				dump_cmd(cmdr, cmd->cmdarg, status,
+					"DTIP Wait Failed");
+				return COMM_ERR;
+			}
+			i++;
+		} while ((status & MMCI_BIT(DTIP)) && i < 10000);
+		if (status & MMCI_BIT(DTIP)) {
+			dump_cmd(cmdr, cmd->cmdarg, status,
+				"XFER DTIP never unset, ignoring");
+		}
+	}
+
+	return 0;
+}
+
+/* entered into mmc structure during driver init */
+static void mci_set_ios(struct mmc *mmc)
+{
+	atmel_mci_t *mci = (atmel_mci_t *)mmc->priv;
+	int busw = (mmc->bus_width == 4) ? 1 : 0;
+
+	/* Set the clock speed */
+	mci_set_mode(mmc, mmc->clock, MMC_DEFAULT_BLKLEN);
+
+	/*
+	 * set the bus width and select slot for this interface
+	 * there is no capability for multiple slots on the same interface yet
+	 * Bitfield SCDBUS needs to be expanded to 2 bits for 8-bit buses
+	 */
+	writel(MMCI_BF(SCDBUS, busw) | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr);
+}
+
+/* entered into mmc structure during driver init */
+static int mci_init(struct mmc *mmc)
+{
+	atmel_mci_t *mci = (atmel_mci_t *)mmc->priv;
+
+	/* Initialize controller */
+	writel(MMCI_BIT(SWRST), &mci->cr);	/* soft reset */
+	writel(MMCI_BIT(PWSDIS), &mci->cr);	/* disable power save */
+	writel(MMCI_BIT(MCIEN), &mci->cr);	/* enable mci */
+
+	/* Initial Time-outs */
+	writel(0x5f, &mci->dtor);
+	/* Disable Interrupts */
+	writel(~0UL, &mci->idr);
+
+	/* Set default clocks and blocklen */
+	mci_set_mode(mmc, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN);
+
+	return 0;
+}
+
+/*
+ * this is the only exported function
+ *
+ * call it with the MCI register base address
+ */
+int atmel_mci_init(void *regs)
+{
+	struct mmc *mmc = malloc(sizeof(struct mmc));
+
+	if (!mmc)
+		return -1;
+	strcpy(mmc->name, "mci");
+	mmc->priv = regs;
+	mmc->send_cmd = mci_send_cmd;
+	mmc->set_ios = mci_set_ios;
+	mmc->init = mci_init;
+
+	/* need to be able to pass these in on a board by board basis */
+	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->host_caps = MMC_MODE_4BIT;
+	/*
+	 * min and max frequencies determined by
+	 * max and min of clock divider
+	 */
+	mmc->f_min = get_mci_clk_rate() / (2*256);
+	mmc->f_max = get_mci_clk_rate() / (2*1);
+
+	mmc_register(mmc);
+
+	return 0;
+}
diff --git a/include/mmc.h b/include/mmc.h
index fcb237e..9f94f42 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -179,6 +179,16 @@ struct mmc_cid {
 	char pnm[7];
 };
 
+/*
+ * WARNING!
+ *
+ * This structure is used by atmel_mci.c only.
+ * It works for the AVR32 architecture but NOT
+ * for ARM/AT91 architectures.
+ * Its use is highly depreciated.
+ * After the atmel_mci.c driver for AVR32 has
+ * been replaced this structure will be removed.
+ */
 struct mmc_csd
 {
 	u8	csd_structure:2,
@@ -275,7 +285,10 @@ int mmc_set_dev(int dev_num);
 void print_mmc_devices(char separator);
 int board_mmc_getcd(u8 *cd, struct mmc *mmc);
 
-#ifndef CONFIG_GENERIC_MMC
+#ifdef CONFIG_GENERIC_MMC
+int atmel_mci_init(void *regs);
+#else
 int mmc_legacy_init(int verbose);
 #endif
+
 #endif /* _MMC_H_ */
-- 
1.5.6.5




More information about the U-Boot mailing list