summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReinhard Meyer <u-boot@emk-elektronik.de>2010-08-13 10:31:06 +0200
committerReinhard Meyer <u-boot@emk-elektronik.de>2010-09-03 11:19:01 +0200
commit1592ef8596fffd937a30462eb15f1d64e237ae49 (patch)
treea4d26cb016750db74bb1585ccb23753632452c53
parentc982d866eaee5e8469af9e22eb8f51c63adcfafa (diff)
AT91: MCI: add SD/MMC driver using mmc framework
Signed-off-by: Reinhard Meyer <u-boot@emk-elektronik.de>
-rw-r--r--arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c21
-rw-r--r--arch/arm/include/asm/arch-at91/at91_common.h1
-rw-r--r--arch/arm/include/asm/arch-at91/clk.h5
-rw-r--r--arch/arm/include/asm/arch-at91/hardware.h1
-rw-r--r--doc/README.atmel_mci86
-rw-r--r--drivers/mmc/Makefile5
-rw-r--r--drivers/mmc/atmel_mci.h48
-rw-r--r--drivers/mmc/gen_atmel_mci.c353
-rw-r--r--include/mmc.h15
9 files changed, 530 insertions, 5 deletions
diff --git a/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c b/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
index 77d49ab1c7..2d8848152a 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 01840eede4..0067190a20 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 f642dd9958..457e6c9b2b 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 4ddb3155d7..9f732a738e 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 0000000000..18b1bdf7e5
--- /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@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 8dfd8a32ba..6603d74294 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-$(CONFIG_S5P_MMC) += s5p_mmc.o
diff --git a/drivers/mmc/atmel_mci.h b/drivers/mmc/atmel_mci.h
index 5b4f5c99b6..823a77d91b 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 0000000000..fa4df9943a
--- /dev/null
+++ b/drivers/mmc/gen_atmel_mci.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2010
+ * Rob Emanuele <rob@emanuele.us>
+ * Reinhard Meyer, EMK Elektronik <reinhard.meyer@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 fcb237e81e..9f94f423fb 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_ */