-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
MicroSD card support for Neo Freerunner U-Boot :-)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org
iD8DBQFHs1A/OjLpvpq7dMoRApO4AJwNOFnbx7WX+Yi0zX8azXEZIYcXLACeLXEG
jYi3nrZush/uGk8Gz6oc8Ks=
=+WNz
-----END PGP SIGNATURE-----
introduce-uboot-glamo-mmc.patch
From: Andy Green <[EMAIL PROTECTED]>
This ports the Glamo MMC driver from Linux to U-Boot.
U-Boot is a lot less welcoming than Linux for MMC, you have
to do your own MMC stack as well as the driver. I took the
opportunity to add SDHC support, at least it works for my
4GB SDHC card in block mode well enough to load and boot
into a kernel from a FAT partition. Its also tested on a
non-SDHC 512MB SD 2.0.0 card in byte addressing mode.
I also enabled and tested 4-bit mode, it always enables it
but it should be okay for any modern microSD card. It also
cranks all cards up to 16MHz, again it should be okay given
the card format is so new. Speed is not bad at around
1MByte/sec read, it's fine for normal use.
Write is implemented but I didn't test it, I doubt it would
see much use in U-Boot.
The card is depowered before exit to Linux so it will be
detected normally there.
I also found in my travels that ext2 support seems broken
in U-Boot but I didn't explore it any further after deciding
it didn't seem to be anything I was doing. FAT support
works though.
I was able to put a kernel and rootfs entirely on the SD card
by fdisking two partitions on there, first a small FAT one for
the kernel uImage and second a large ext2 one for the rootfs.
This worked fine and surprising quickly booted to a shell
prompt entirely from microSD. I had to configure Linux to have
EXT2 support in the monolithic kernel.
To do this kind of thing use the following U-Boot type commands
(assumes a file "kernel" in top level directory of first
partition on card is the U-Boot kernel image)
mmcinit
fatload mmc 1 0x32000000 kernel
setenv bootargs rootfstype=vfat root=/dev/mmcblk0p1 console=ttySAC2,115200 console=tty0 loglevel=2
bootm 0x32000000
The existing setup had CONFIG_MMC associate only with the S3C
driver from GTA01 so I added an additional CONFIG_ to tell
between them for GTA01 and GTA02 configs.
Signed-off-by: Andy Green <[EMAIL PROTECTED]>
---
board/neo1973/gta02/Makefile | 2
board/neo1973/gta02/glamo-mmc.c | 826 +++++++++++++++++++++++++++++++++++++++
board/neo1973/gta02/glamo-mmc.h | 149 +++++++
common/cmd_bootm.c | 6
cpu/arm920t/s3c24x0/mmc.c | 4
include/configs/neo1973_gta01.h | 1
include/configs/neo1973_gta02.h | 5
7 files changed, 989 insertions(+), 4 deletions(-)
create mode 100644 board/neo1973/gta02/glamo-mmc.c
create mode 100644 board/neo1973/gta02/glamo-mmc.h
diff --git a/board/neo1973/gta02/Makefile b/board/neo1973/gta02/Makefile
index 27b2d89..be8b7f6 100644
--- a/board/neo1973/gta02/Makefile
+++ b/board/neo1973/gta02/Makefile
@@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk
LIB = lib$(BOARD).a
-OBJS := gta02.o pcf50633.o nand.o nor.o ../common/cmd_neo1973.o \
+OBJS := gta02.o pcf50633.o nand.o nor.o glamo-mmc.o ../common/cmd_neo1973.o \
../common/gsmver.o \
../common/jbt6k74.o ../common/udc.o ../common/bootmenu.o
SOBJS := ../common/lowlevel_init.o
diff --git a/board/neo1973/gta02/glamo-mmc.c b/board/neo1973/gta02/glamo-mmc.c
new file mode 100644
index 0000000..0413242
--- /dev/null
+++ b/board/neo1973/gta02/glamo-mmc.c
@@ -0,0 +1,826 @@
+/*
+ * linux/drivers/mmc/host/glamo-mmc.c - Glamo MMC driver
+ *
+ * Copyright (C) 2007 OpenMoko, Inc, Andy Green <[EMAIL PROTECTED]>
+ * Based on the Glamo MCI driver that was -->
+ *
+ * Copyright (C) 2007 OpenMoko, Inc, Andy Green <[EMAIL PROTECTED]>
+ * Based on S3C MMC driver that was:
+ * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <[EMAIL PROTECTED]>
+ *
+ * and
+ *
+ * Based on S3C MMC driver that was (original copyright notice ---->)
+ *
+ * (C) Copyright 2006 by OpenMoko, Inc.
+ * Author: Harald Welte <[EMAIL PROTECTED]>
+ *
+ * based on u-boot pxa MMC driver and linux/drivers/mmc/s3c2410mci.c
+ * (C) 2005-2005 Thomas Kleffel
+ *
+ * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <[EMAIL PROTECTED]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <config.h>
+#include <common.h>
+#include <mmc.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <part.h>
+#include <fat.h>
+#include <pcf50633.h>
+
+#include "glamo-regs.h"
+#include "glamo-mmc.h"
+
+#if defined(CONFIG_MMC) && defined(CONFIG_MMC_GLAMO)
+
+#define MMC_BLOCK_SIZE_BITS 9
+#define MMC_BLOCK_SIZE (1 << MMC_BLOCK_SIZE_BITS)
+
+#define GLAMO_REG(x) (*(volatile u16 *)(CONFIG_GLAMO_BASE + x))
+#define GLAMO_INTRAM_OFFSET (8 * 1024 * 1024)
+#define GLAMO_FB_SIZE ((8 * 1024 * 1024) - 0x10000)
+#define GLAMO_START_OF_MMC_INTMEM ((volatile u16 *)(CONFIG_GLAMO_BASE + \
+ GLAMO_INTRAM_OFFSET + GLAMO_FB_SIZE))
+
+static int ccnt;
+static block_dev_desc_t mmc_dev;
+static mmc_csd_t mmc_csd;
+static int mmc_ready = 0;
+static int wide = 0;
+static enum card_type card_type = CARDTYPE_NONE;
+
+block_dev_desc_t * mmc_get_dev(int dev)
+{
+ return (block_dev_desc_t *)&mmc_dev;
+}
+
+static void
+glamo_reg_write(u_int16_t val, u_int16_t reg)
+{
+ GLAMO_REG(reg) = val;
+}
+
+static u_int16_t
+glamo_reg_read(u_int16_t reg)
+{
+ return GLAMO_REG(reg);
+}
+
+unsigned char CRC7(u8 * pu8, int cnt)
+{
+ u8 crc = 0;
+
+ while (cnt--) {
+ int n;
+ u8 d = *pu8++;
+ for (n = 0; n < 8; n++) {
+ crc <<= 1;
+ if ((d & 0x80) ^ (crc & 0x80))
+ crc ^= 0x09;
+ d <<= 1;
+ }
+ }
+ return (crc << 1) | 1;
+}
+
+ulong mmc_bread(int dev_num, ulong blknr, ulong blkcnt, void *dst)
+{
+ ulong src = blknr * MMC_BLOCK_SIZE;
+
+ if (!blkcnt)
+ return 0;
+
+/* printf("mmc_bread(%d, %ld, %ld, %p)\n", dev_num, blknr, blkcnt, dst); */
+ mmc_read(src, dst, blkcnt * MMC_BLOCK_SIZE);
+ return blkcnt;
+}
+
+/* MMC_DEFAULT_RCA should probably be just 1, but this may break other code
+ that expects it to be shifted. */
+static u_int16_t rca = MMC_DEFAULT_RCA >> 16;
+
+static void do_pio_read(u16 *buf, int count_words)
+{
+ volatile u16 *from_ptr = GLAMO_START_OF_MMC_INTMEM;
+
+ while (count_words--)
+ *buf++ = *from_ptr++;
+}
+
+static void do_pio_write(u16 *buf, int count_words)
+{
+ volatile u16 *to_ptr = GLAMO_START_OF_MMC_INTMEM;
+
+ while (count_words--)
+ *to_ptr++ = *buf++;
+}
+
+
+static int mmc_cmd(int opcode, int arg, int flags,
+ int data_size, int data_blocks,
+ int will_stop, u16 *resp)
+{
+ u16 * pu16 = (u16 *)&resp[0];
+ u16 * reg_resp = (u16 *)(CONFIG_GLAMO_BASE + GLAMO_REGOFS_MMC +
+ GLAMO_REG_MMC_CMD_RSP1);
+ u16 status;
+ int n;
+ u8 u8a[6];
+ u16 fire = 0;
+ int cmd_is_stop = 0;
+ int error = 0;
+
+#if 0
+ printf("mmc_cmd(opcode=%d, arg=0x%08X, flags=0x%x, "
+ "data_size=%d, data_blocks=%d, will_stop=%d, resp=%p)\n",
+ opcode, arg, flags, data_size, data_blocks, will_stop, resp);
+#endif
+ switch (opcode) {
+ case MMC_STOP_TRANSMISSION:
+ cmd_is_stop = 1;
+ break;
+ default:
+ break;
+ }
+
+ ccnt++;
+
+ /* this guy has data to read/write? */
+ if ((!cmd_is_stop) && (flags & (MMC_DATA_WRITE | MMC_DATA_READ))) {
+ /*
+ * the S-Media-internal RAM offset for our MMC buffer
+ */
+ glamo_reg_write((u16)GLAMO_FB_SIZE,
+ GLAMO_REGOFS_MMC + GLAMO_REG_MMC_WDATADS1);
+ glamo_reg_write((u16)(GLAMO_FB_SIZE >> 16),
+ GLAMO_REGOFS_MMC + GLAMO_REG_MMC_WDATADS2);
+ glamo_reg_write((u16)GLAMO_FB_SIZE,
+ GLAMO_REGOFS_MMC + GLAMO_REG_MMC_RDATADS1);
+ glamo_reg_write((u16)(GLAMO_FB_SIZE >> 16),
+ GLAMO_REGOFS_MMC + GLAMO_REG_MMC_RDATADS2);
+
+ /* set up the block info */
+ glamo_reg_write(data_size, GLAMO_REGOFS_MMC +
+ GLAMO_REG_MMC_DATBLKLEN);
+ glamo_reg_write(data_blocks, GLAMO_REGOFS_MMC +
+ GLAMO_REG_MMC_DATBLKCNT);
+ }
+
+ /* if we can't do it, reject as busy */
+ if (!glamo_reg_read(GLAMO_REGOFS_MMC + GLAMO_REG_MMC_RB_STAT1) &
+ GLAMO_STAT1_MMC_IDLE)
+ return -1;
+
+ /* create an array in wire order for CRC computation */
+ u8a[0] = 0x40 | (opcode & 0x3f);
+ u8a[1] = (arg >> 24);
+ u8a[2] = (arg >> 16);
+ u8a[3] = (arg >> 8);
+ u8a[4] = arg;
+ u8a[5] = CRC7(&u8a[0], 5); /* CRC7 on first 5 bytes of packet */
+
+ /* issue the wire-order array including CRC in register order */
+ glamo_reg_write((u8a[4] << 8) | u8a[5],
+ GLAMO_REGOFS_MMC + GLAMO_REG_MMC_CMD_REG1);
+ glamo_reg_write((u8a[2] << 8) | u8a[3],
+ GLAMO_REGOFS_MMC + GLAMO_REG_MMC_CMD_REG2);
+ glamo_reg_write((u8a[0] << 8) | u8a[1],
+ GLAMO_REGOFS_MMC + GLAMO_REG_MMC_CMD_REG3);
+
+ /* command index toggle */
+ fire |= (ccnt & 1) << 12;
+
+ /* set type of command */
+ switch (mmc_cmd_type(flags)) {
+ case MMC_CMD_BC:
+ fire |= GLAMO_FIRE_MMC_CMDT_BNR;
+ break;
+ case MMC_CMD_BCR:
+ fire |= GLAMO_FIRE_MMC_CMDT_BR;
+ break;
+ case MMC_CMD_AC:
+ fire |= GLAMO_FIRE_MMC_CMDT_AND;
+ break;
+ case MMC_CMD_ADTC:
+ fire |= GLAMO_FIRE_MMC_CMDT_AD;
+ break;
+ }
+ /*
+ * if it expects a response, set the type expected
+ *
+ * R1, Length : 48bit, Normal response
+ * R1b, Length : 48bit, same R1, but added card busy status
+ * R2, Length : 136bit (really 128 bits with CRC snipped)
+ * R3, Length : 48bit (OCR register value)
+ * R4, Length : 48bit, SDIO_OP_CONDITION, Reverse SDIO Card
+ * R5, Length : 48bit, IO_RW_DIRECTION, Reverse SDIO Card
+ * R6, Length : 48bit (RCA register)
+ * R7, Length : 48bit (interface condition, VHS(voltage supplied),
+ * check pattern, CRC7)
+ */
+ switch (mmc_resp_type(flags)) {
+ case MMC_RSP_R6: /* same index as R7 and R1 */
+ fire |= GLAMO_FIRE_MMC_RSPT_R1;
+ break;
+ case MMC_RSP_R1B:
+ fire |= GLAMO_FIRE_MMC_RSPT_R1b;
+ break;
+ case MMC_RSP_R2:
+ fire |= GLAMO_FIRE_MMC_RSPT_R2;
+ break;
+ case MMC_RSP_R3:
+ fire |= GLAMO_FIRE_MMC_RSPT_R3;
+ break;
+ /* R4 and R5 supported by chip not defined in linux/mmc/core.h (sdio) */
+ }
+ /*
+ * From the command index, set up the command class in the host ctrllr
+ *
+ * missing guys present on chip but couldn't figure out how to use yet:
+ * 0x0 "stream read"
+ * 0x9 "cancel running command"
+ */
+ switch (opcode) {
+ case MMC_READ_SINGLE_BLOCK:
+ fire |= GLAMO_FIRE_MMC_CC_SBR; /* single block read */
+ break;
+ case MMC_SWITCH: /* 64 byte payload */
+ case 0x33: /* observed issued by MCI */
+ case MMC_READ_MULTIPLE_BLOCK:
+ /* we will get an interrupt off this */
+ if (!will_stop)
+ /* multiblock no stop */
+ fire |= GLAMO_FIRE_MMC_CC_MBRNS;
+ else
+ /* multiblock with stop */
+ fire |= GLAMO_FIRE_MMC_CC_MBRS;
+ break;
+ case MMC_WRITE_BLOCK:
+ fire |= GLAMO_FIRE_MMC_CC_SBW; /* single block write */
+ break;
+ case MMC_WRITE_MULTIPLE_BLOCK:
+ if (will_stop)
+ /* multiblock with stop */
+ fire |= GLAMO_FIRE_MMC_CC_MBWS;
+ else
+ /* multiblock NO stop-- 'RESERVED'? */
+ fire |= GLAMO_FIRE_MMC_CC_MBWNS;
+ break;
+ case MMC_STOP_TRANSMISSION:
+ fire |= GLAMO_FIRE_MMC_CC_STOP; /* STOP */
+ break;
+ default:
+ fire |= GLAMO_FIRE_MMC_CC_BASIC; /* "basic command" */
+ break;
+ }
+ /* enforce timeout */
+ glamo_reg_write(0xfff, GLAMO_REGOFS_MMC + GLAMO_REG_MMC_TIMEOUT);
+
+ /* Generate interrupt on txfer; drive strength max */
+ glamo_reg_write((glamo_reg_read(GLAMO_REGOFS_MMC +
+ GLAMO_REG_MMC_BASIC) & 0xfe) |
+ 0x0800 | GLAMO_BASIC_MMC_NO_CLK_RD_WAIT |
+ GLAMO_BASIC_MMC_EN_COMPL_INT |
+ GLAMO_BASIC_MMC_EN_DR_STR0 |
+ GLAMO_BASIC_MMC_EN_DR_STR1,
+ GLAMO_REGOFS_MMC + GLAMO_REG_MMC_BASIC);
+
+ /* send the command out on the wire */
+ /* dev_info(&host->pdev->dev, "Using FIRE %04X\n", fire); */
+ glamo_reg_write(fire, GLAMO_REGOFS_MMC + GLAMO_REG_MMC_CMD_FIRE);
+
+ /*
+ * we must spin until response is ready or timed out
+ * -- we don't get interrupts unless there is a bulk rx
+ */
+ do
+ status = glamo_reg_read(GLAMO_REGOFS_MMC +
+ GLAMO_REG_MMC_RB_STAT1);
+ while ((((status >> 15) & 1) != (ccnt & 1)) ||
+ (!(status & (GLAMO_STAT1_MMC_RB_RRDY |
+ GLAMO_STAT1_MMC_RTOUT |
+ GLAMO_STAT1_MMC_DTOUT |
+ GLAMO_STAT1_MMC_BWERR |
+ GLAMO_STAT1_MMC_BRERR))));
+
+ if (status & (GLAMO_STAT1_MMC_RTOUT | GLAMO_STAT1_MMC_DTOUT))
+ error = -4;
+ if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR))
+ error = -5;
+
+ if (cmd_is_stop)
+ return 0;
+
+ if (error) {
+ printf("cmd 0x%x, arg 0x%x flags 0x%x\n", opcode, arg, flags);
+ printf("Error after cmd: 0x%x\n", error);
+ goto done;
+ }
+ /*
+ * mangle the response registers in two different exciting
+ * undocumented ways discovered by trial and error
+ */
+ if (mmc_resp_type(flags) == MMC_RSP_R2)
+ /* grab the response */
+ for (n = 0; n < 8; n++) /* super mangle power 1 */
+ pu16[n ^ 6] = reg_resp[n];
+ else
+ for (n = 0; n < 3; n++) /* super mangle power 2 */
+ pu16[n] = (reg_resp[n] >> 8) |
+ (reg_resp[n + 1] << 8);
+ /*
+ * if we don't have bulk data to take care of, we're done
+ */
+ if (!(flags & (MMC_DATA_READ | MMC_DATA_WRITE)))
+ goto done;
+
+ /* enforce timeout */
+ glamo_reg_write(0xfff, GLAMO_REGOFS_MMC + GLAMO_REG_MMC_TIMEOUT);
+ /*
+ * spin
+ */
+ while (!(glamo_reg_read(GLAMO_REG_IRQ_STATUS) & GLAMO_IRQ_MMC))
+ ;
+ /* ack this interrupt source */
+ glamo_reg_write(GLAMO_IRQ_MMC, GLAMO_REG_IRQ_CLEAR);
+
+ if (status & GLAMO_STAT1_MMC_DTOUT)
+ error = -1;
+ if (status & (GLAMO_STAT1_MMC_BWERR | GLAMO_STAT1_MMC_BRERR))
+ error = -2;
+ if (status & GLAMO_STAT1_MMC_RTOUT)
+ error = -5;
+ if (error) {
+ printf("cmd 0x%x, arg 0x%x flags 0x%x\n", opcode, arg, flags);
+ printf("Error after resp: 0x%x\n", status);
+ goto done;
+ }
+#if 0
+ if (flags & MMC_DATA_READ) {
+ volatile u8 * pu8 = (volatile u8 *)GLAMO_START_OF_MMC_INTMEM;
+ for (n = 0; n < 512; n += 16) {
+ int n1;
+ for (n1 = 0; n1 < 16; n1++) {
+ printf("%02X ", pu8[n + n1]);
+ }
+ printf("\n");
+ }
+ }
+#endif
+ return 0;
+
+done:
+ return error;
+}
+
+static void glamo_mci_reset(void)
+{
+ /* reset MMC controller */
+ glamo_reg_write(GLAMO_CLOCK_MMC_RESET | GLAMO_CLOCK_MMC_DG_TCLK |
+ GLAMO_CLOCK_MMC_EN_TCLK | GLAMO_CLOCK_MMC_DG_M9CLK |
+ GLAMO_CLOCK_MMC_EN_M9CLK,
+ GLAMO_REG_CLOCK_MMC);
+ udelay(100000);
+ /* and disable reset */
+ glamo_reg_write(GLAMO_CLOCK_MMC_DG_TCLK |
+ GLAMO_CLOCK_MMC_EN_TCLK | GLAMO_CLOCK_MMC_DG_M9CLK |
+ GLAMO_CLOCK_MMC_EN_M9CLK,
+ GLAMO_REG_CLOCK_MMC);
+}
+
+
+static u_int8_t ldo_voltage(unsigned int millivolts)
+{
+ if (millivolts < 900)
+ return 0;
+ else if (millivolts > 3600)
+ return 0x1f;
+
+ millivolts -= 900;
+ return millivolts / 100;
+}
+
+int mmc_read(ulong src, uchar *dst, int size)
+{
+ int resp;
+ u8 response[16];
+ int size_original = size;
+
+ if ((!size) || (size & (MMC_BLOCK_SIZE - 1))) {
+ printf("Bad size %d\n", size);
+ return 0;
+ }
+
+ if (((int)dst) & 1) {
+ printf("Bad align on dst\n");
+ return 0;
+ }
+
+ resp = mmc_cmd(MMC_SET_BLOCKLEN, MMC_BLOCK_SIZE,
+ MMC_CMD_AC | MMC_RSP_R1, 0, 0, 0,
+ (u16 *)&response[0]);
+
+ while (size) {
+ switch (card_type) {
+ case CARDTYPE_SDHC: /* block addressing */
+ resp = mmc_cmd(MMC_READ_SINGLE_BLOCK,
+ src >> MMC_BLOCK_SIZE_BITS,
+ MMC_CMD_ADTC | MMC_RSP_R1 |
+ MMC_DATA_READ, MMC_BLOCK_SIZE, 1, 0,
+ (u16 *)&response[0]);
+ break;
+ default: /* byte addressing */
+ resp = mmc_cmd(MMC_READ_SINGLE_BLOCK, src,
+ MMC_CMD_ADTC | MMC_RSP_R1 | MMC_DATA_READ,
+ MMC_BLOCK_SIZE, 1, 0,
+ (u16 *)&response[0]);
+ break;
+ }
+ do_pio_read((u16 *)dst, MMC_BLOCK_SIZE >> 1);
+
+ if (size >= MMC_BLOCK_SIZE)
+ size -= MMC_BLOCK_SIZE;
+ else
+ size = 0;
+ dst += MMC_BLOCK_SIZE;
+ src += MMC_BLOCK_SIZE;
+ }
+ return size_original;
+}
+
+int mmc_write(uchar *src, ulong dst, int size)
+{
+ int resp;
+ u8 response[16];
+ int size_original = size;
+
+ if ((!size) || (size & (MMC_BLOCK_SIZE - 1))) {
+ printf("Bad size %d\n", size);
+ return 0;
+ }
+
+ if (((int)dst) & 1) {
+ printf("Bad align on dst\n");
+ return 0;
+ }
+
+ resp = mmc_cmd(MMC_SET_BLOCKLEN, MMC_BLOCK_SIZE,
+ MMC_CMD_AC | MMC_RSP_R1, 0, 0, 0,
+ (u16 *)&response[0]);
+
+ while (size) {
+ do_pio_write((u16 *)src, MMC_BLOCK_SIZE >> 1);
+ switch (card_type) {
+ case CARDTYPE_SDHC: /* block addressing */
+ resp = mmc_cmd(MMC_WRITE_BLOCK,
+ dst >> MMC_BLOCK_SIZE_BITS,
+ MMC_CMD_ADTC | MMC_RSP_R1 |
+ MMC_DATA_WRITE,
+ MMC_BLOCK_SIZE, 1, 0,
+ (u16 *)&response[0]);
+ break;
+ default: /* byte addressing */
+ resp = mmc_cmd(MMC_WRITE_BLOCK, dst,
+ MMC_CMD_ADTC | MMC_RSP_R1 |
+ MMC_DATA_WRITE,
+ MMC_BLOCK_SIZE, 1, 0,
+ (u16 *)&response[0]);
+ break;
+ }
+ if (size >= MMC_BLOCK_SIZE)
+ size -= MMC_BLOCK_SIZE;
+ else
+ size = 0;
+ dst += MMC_BLOCK_SIZE;
+ src += MMC_BLOCK_SIZE;
+ }
+ return size_original;
+}
+
+static void print_mmc_cid(mmc_cid_t *cid)
+{
+ printf("MMC found. Card desciption is:\n");
+ printf("Manufacturer ID = %02x%02x%02x\n",
+ cid->id[0], cid->id[1], cid->id[2]);
+ printf("HW/FW Revision = %x %x\n",cid->hwrev, cid->fwrev);
+ cid->hwrev = cid->fwrev = 0; /* null terminate string */
+ printf("Product Name = %s\n",cid->name);
+ printf("Serial Number = %02x%02x%02x\n",
+ cid->sn[0], cid->sn[1], cid->sn[2]);
+ printf("Month = %d\n",cid->month);
+ printf("Year = %d\n",1997 + cid->year);
+}
+
+static void print_sd_cid(const struct sd_cid *cid)
+{
+ printf("Card Type: ");
+ switch (card_type) {
+ case CARDTYPE_NONE:
+ printf("(None)\n");
+ break;
+ case CARDTYPE_MMC:
+ printf("MMC\n");
+ break;
+ case CARDTYPE_SD:
+ printf("SD\n");
+ break;
+ case CARDTYPE_SD20:
+ printf("SD 2.0\n");
+ break;
+ case CARDTYPE_SDHC:
+ printf("SD 2.0 SDHC\n");
+ break;
+ }
+ printf("Manufacturer: 0x%02x, OEM \"%c%c\"\n",
+ cid->mid, cid->oid_0, cid->oid_1);
+ printf("Product name: \"%c%c%c%c%c\", revision %d.%d\n",
+ cid->pnm_0, cid->pnm_1, cid->pnm_2, cid->pnm_3, cid->pnm_4,
+ cid->prv >> 4, cid->prv & 15);
+ printf("Serial number: %u\n",
+ cid->psn_0 << 24 | cid->psn_1 << 16 | cid->psn_2 << 8 |
+ cid->psn_3);
+ printf("Manufacturing date: %d/%d\n",
+ cid->mdt_1 & 15,
+ 2000+((cid->mdt_0 & 15) << 4)+((cid->mdt_1 & 0xf0) >> 4));
+/* printf("CRC: 0x%02x, b0 = %d\n",
+ cid->crc >> 1, cid->crc & 1); */
+}
+
+
+int mmc_init(int verbose)
+{
+ int retries = 14, rc = -ENODEV;
+ int resp, count_reset = 0;
+ u8 response[16];
+ mmc_cid_t *mmc_cid = (mmc_cid_t *)response;
+ struct sd_cid *sd_cid = (struct sd_cid *)response;
+ u32 hcs = 0;
+
+ card_type = CARDTYPE_NONE;
+
+ /* enable engine */
+
+ glamo_reg_write(GLAMO_CLOCK_MMC_EN_M9CLK |
+ GLAMO_CLOCK_MMC_EN_TCLK |
+ GLAMO_CLOCK_MMC_DG_M9CLK |
+ GLAMO_CLOCK_MMC_DG_TCLK, GLAMO_REG_CLOCK_MMC);
+ glamo_reg_write(glamo_reg_read(GLAMO_REG_HOSTBUS(2)) |
+ GLAMO_HOSTBUS2_MMIO_EN_MMC, GLAMO_REG_HOSTBUS(2));
+
+ /* controller reset */
+
+ glamo_mci_reset();
+
+ /* power the sdcard slot */
+
+ pcf50633_reg_write(PCF50633_REG_HCLDOOUT, ldo_voltage(3300));
+ udelay(10000);
+ pcf50633_reg_write(PCF50633_REG_HCLDOOUT + 1,
+ pcf50633_reg_read(PCF50633_REG_HCLDOOUT + 1) | 1); /* on */
+ udelay(10000);
+
+ /* start the clock -- slowly (50MHz / 250 == 195kHz */
+
+ glamo_reg_write((glamo_reg_read(GLAMO_REG_CLOCK_GEN8) & 0xff00) | 250,
+ GLAMO_REG_CLOCK_GEN8);
+
+ /* enable clock to divider input */
+
+ glamo_reg_write(glamo_reg_read(
+ GLAMO_REG_CLOCK_GEN5_1) | GLAMO_CLOCK_GEN51_EN_DIV_TCLK,
+ GLAMO_REG_CLOCK_GEN5_1);
+
+ udelay(100000);
+
+ /* set bus width to 1 */
+
+ glamo_reg_write((glamo_reg_read(GLAMO_REGOFS_MMC +
+ GLAMO_REG_MMC_BASIC) &
+ (~GLAMO_BASIC_MMC_EN_4BIT_DATA)),
+ GLAMO_REGOFS_MMC + GLAMO_REG_MMC_BASIC);
+
+ /* reset */
+
+ resp = mmc_cmd(MMC_GO_IDLE_STATE, 0, MMC_CMD_BCR, 0, 0, 0,
+ (u16 *)&response[0]);
+
+ udelay(100000);
+
+ /* SDHC card? */
+
+ resp = mmc_cmd(SD_SEND_IF_COND, 0x000001aa,
+ MMC_CMD_BCR | MMC_RSP_R7, 0, 0, 0,
+ (u16 *)&response[0]);
+ if (!resp && (response[0] == 0xaa)) {
+ card_type = CARDTYPE_SD20; /* 2.0 SD, may not be SDHC */
+ hcs = 0x40000000;
+ }
+
+ /* Well, either way let's say hello in SD card protocol */
+
+ while (retries--) {
+
+ udelay(100000);
+
+ resp = mmc_cmd(MMC_APP_CMD, 0x00000000,
+ MMC_CMD_AC | MMC_RSP_R1, 0, 0, 0,
+ (u16 *)&response[0]);
+ if (resp)
+ continue;
+ resp = mmc_cmd(SD_APP_OP_COND, hcs | 0x00300000,
+ MMC_CMD_BCR | MMC_RSP_R3, 0, 0, 0,
+ (u16 *)&response[0]);
+ if (resp)
+ continue;
+
+ if (response[3] & (1 << 6)) /* asserts block addressing */
+ card_type = CARDTYPE_SDHC;
+
+ if (response[3] & (1 << 7)) { /* not busy */
+ if (card_type == CARDTYPE_NONE)
+ card_type = CARDTYPE_SD;
+ break;
+ }
+ }
+ if (retries < 0)
+ return 1;
+
+ if (card_type == CARDTYPE_NONE) {
+ retries = 10;
+ printf("failed to detect SD Card, trying MMC\n");
+ do {
+ resp = mmc_cmd(MMC_SEND_OP_COND, 0x00ffc000,
+ MMC_CMD_BCR | MMC_RSP_R3, 0, 0, 0,
+ (u16 *)&response[0]);
+ debug("resp %x %x\n", response[0], response[1]);
+ udelay(50);
+ } while (retries-- && !(response[3] & 0x80));
+ if (retries >= 0)
+ card_type = CARDTYPE_MMC;
+ else
+ return 1;
+ }
+
+ /* fill in device description */
+ mmc_dev.if_type = IF_TYPE_MMC;
+ mmc_dev.part_type = PART_TYPE_DOS;
+ mmc_dev.dev = 0;
+ mmc_dev.lun = 0;
+ mmc_dev.type = 0;
+ mmc_dev.removable = 0;
+ mmc_dev.block_read = mmc_bread;
+ mmc_dev.blksz = 512;
+ mmc_dev.lba = 1 << 16; /* 64K x 512 blocks = 32MB default */
+
+ /* try to get card id */
+ resp = mmc_cmd(MMC_ALL_SEND_CID, hcs,
+ MMC_CMD_BCR | MMC_RSP_R2, 0, 0, 0,
+ (u16 *)&response[0]);
+ if (resp)
+ return 1;
+
+ switch (card_type) {
+ case CARDTYPE_MMC:
+ /* TODO configure mmc driver depending on card
+ attributes */
+
+ if (verbose)
+ print_mmc_cid(mmc_cid);
+ sprintf((char *) mmc_dev.vendor,
+ "Man %02x%02x%02x Snr %02x%02x%02x",
+ mmc_cid->id[0], mmc_cid->id[1], mmc_cid->id[2],
+ mmc_cid->sn[0], mmc_cid->sn[1], mmc_cid->sn[2]);
+ sprintf((char *) mmc_dev.product, "%s", mmc_cid->name);
+ sprintf((char *) mmc_dev.revision, "%x %x",
+ mmc_cid->hwrev, mmc_cid->fwrev);
+
+ /* MMC exists, get CSD too */
+ resp = mmc_cmd(MMC_SET_RELATIVE_ADDR, MMC_DEFAULT_RCA,
+ MMC_CMD_AC | MMC_RSP_R1, 0, 0, 0,
+ (u16 *)&response[0]);
+ break;
+
+ case CARDTYPE_SD:
+ case CARDTYPE_SD20:
+ case CARDTYPE_SDHC:
+ if (verbose)
+ print_sd_cid(sd_cid);
+ sprintf((char *) mmc_dev.vendor,
+ "Man %02 OEM %c%c \"%c%c%c%c%c\"",
+ sd_cid->mid, sd_cid->oid_0, sd_cid->oid_1,
+ sd_cid->pnm_0, sd_cid->pnm_1, sd_cid->pnm_2,
+ sd_cid->pnm_3, sd_cid->pnm_4);
+ sprintf((char *) mmc_dev.product, "%d",
+ sd_cid->psn_0 << 24 | sd_cid->psn_1 << 16 |
+ sd_cid->psn_2 << 8 | sd_cid->psn_3);
+ sprintf((char *) mmc_dev.revision, "%d.%d",
+ sd_cid->prv >> 4, sd_cid->prv & 15);
+
+ resp = mmc_cmd(SD_SEND_RELATIVE_ADDR, MMC_DEFAULT_RCA,
+ MMC_CMD_BCR | MMC_RSP_R6, 0, 0, 0,
+ (u16 *)&response[0]);
+ rca = response[2] | (response[3] << 8);
+ break;
+
+ default:
+ return 1;
+ }
+
+ /* grab the CSD */
+
+ resp = mmc_cmd(MMC_SEND_CSD, rca << 16,
+ MMC_CMD_AC | MMC_RSP_R2, 0, 0, 0,
+ (u16 *)&response[0]);
+ if (!resp) {
+ mmc_csd_t *csd = (mmc_csd_t *)response;
+
+ memcpy(&mmc_csd, csd, sizeof(csd));
+ rc = 0;
+ mmc_ready = 1;
+ /* FIXME add verbose printout for csd */
+ /* printf("READ_BL_LEN=%u, C_SIZE_MULT=%u, C_SIZE=%u\n",
+ csd->read_bl_len, csd->c_size_mult1,
+ csd->c_size); */
+ mmc_dev.blksz = 512;
+ mmc_dev.lba = (((unsigned long)1 << csd->c_size_mult1) *
+ (unsigned long)csd->c_size) >> 9;
+ printf("MMC/SD size: %dMiB\n", mmc_dev.lba >> 1);
+ }
+
+ resp = mmc_cmd(MMC_SELECT_CARD, rca<<16, MMC_CMD_AC | MMC_RSP_R1,
+ 0, 0, 0, (u16 *)&response[0]);
+ if (resp)
+ return 1;
+
+#ifdef CONFIG_MMC_WIDE
+ /* yay 4-bit! */
+ if (card_type == CARDTYPE_SD || card_type == CARDTYPE_SDHC) {
+ resp = mmc_cmd(MMC_APP_CMD, rca<<16, MMC_CMD_AC | MMC_RSP_R1,
+ 0, 0, 0, (u16 *)&response[0]);
+ resp = mmc_cmd(MMC_SWITCH, 0x02, MMC_CMD_AC | MMC_RSP_R1B,
+ 0, 0, 0, (u16 *)&response[0]);
+ wide = 1;
+ glamo_reg_write(glamo_reg_read(GLAMO_REGOFS_MMC +
+ GLAMO_REG_MMC_BASIC) | GLAMO_BASIC_MMC_EN_4BIT_DATA,
+ GLAMO_REGOFS_MMC + GLAMO_REG_MMC_BASIC);
+ }
+#endif
+
+ /* crank the clock to the final speed, 16MHz */
+
+ glamo_reg_write((glamo_reg_read(GLAMO_REG_CLOCK_GEN8) & 0xff00) | 2,
+ GLAMO_REG_CLOCK_GEN8);
+
+ fat_register_device(&mmc_dev, 1); /* partitions start counting with 1 */
+
+ return rc;
+}
+
+void mmc_depower(void)
+{
+ u8 response[16];
+
+ /* reset */
+ mmc_cmd(MMC_GO_IDLE_STATE, 0, MMC_CMD_BCR, 0, 0, 0,
+ (u16 *)&response[0]);
+
+ /* hold engine reset, remove clocks */
+
+ glamo_reg_write(GLAMO_CLOCK_MMC_RESET, GLAMO_REG_CLOCK_MMC);
+
+ /* disable engine */
+
+ glamo_reg_write(0, GLAMO_REG_CLOCK_MMC);
+ glamo_reg_write(glamo_reg_read(GLAMO_REG_HOSTBUS(2)) &
+ (~GLAMO_HOSTBUS2_MMIO_EN_MMC), GLAMO_REG_HOSTBUS(2));
+
+ /* remove power */
+
+ pcf50633_reg_write(PCF50633_REG_HCLDOOUT + 1,
+ pcf50633_reg_read(PCF50633_REG_HCLDOOUT + 1) & ~1); /* off */
+}
+
+int
+mmc_ident(block_dev_desc_t *dev)
+{
+ return 0;
+}
+
+int
+mmc2info(ulong addr)
+{
+ /* FIXME hard codes to 32 MB device */
+ if (addr >= CFG_MMC_BASE && addr < CFG_MMC_BASE + 0x02000000)
+ return 1;
+
+ return 0;
+}
+
+
+#endif /* defined(CONFIG_MMC) && defined(CONFIG_MMC_GLAMO) */
diff --git a/board/neo1973/gta02/glamo-mmc.h b/board/neo1973/gta02/glamo-mmc.h
new file mode 100644
index 0000000..3f2294c
--- /dev/null
+++ b/board/neo1973/gta02/glamo-mmc.h
@@ -0,0 +1,149 @@
+#ifndef __GLAMO_MMC_H__
+#define __GLAMO_MMC_H__
+
+/* Standard MMC commands (4.1) type argument response */
+ /* class 1 */
+#define MMC_GO_IDLE_STATE 0 /* bc */
+#define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */
+#define MMC_ALL_SEND_CID 2 /* bcr R2 */
+#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */
+#define MMC_SET_DSR 4 /* bc [31:16] RCA */
+#define MMC_SWITCH 6 /* ac [31:0] See below R1b */
+#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */
+#define MMC_SEND_EXT_CSD 8 /* adtc R1 */
+#define MMC_SEND_CSD 9 /* ac [31:16] RCA R2 */
+#define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */
+#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */
+#define MMC_STOP_TRANSMISSION 12 /* ac R1b */
+#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */
+#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */
+#define MMC_SPI_READ_OCR 58 /* spi spi_R3 */
+#define MMC_SPI_CRC_ON_OFF 59 /* spi [0:0] flag spi_R1 */
+
+#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
+#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
+
+ /* class 2 */
+#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */
+#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */
+#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
+
+ /* class 3 */
+#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
+
+ /* class 4 */
+#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */
+#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */
+#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */
+#define MMC_PROGRAM_CID 26 /* adtc R1 */
+#define MMC_PROGRAM_CSD 27 /* adtc R1 */
+
+ /* class 6 */
+#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */
+#define MMC_CLR_WRITE_PROT 29 /* ac [31:0] data addr R1b */
+#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1 */
+
+ /* class 5 */
+#define MMC_ERASE_GROUP_START 35 /* ac [31:0] data addr R1 */
+#define MMC_ERASE_GROUP_END 36 /* ac [31:0] data addr R1 */
+#define MMC_ERASE 38 /* ac R1b */
+
+ /* class 9 */
+#define MMC_FAST_IO 39 /* ac <Complex> R4 */
+#define MMC_GO_IRQ_STATE 40 /* bcr R5 */
+
+ /* class 7 */
+#define MMC_LOCK_UNLOCK 42 /* adtc R1b */
+
+ /* class 8 */
+#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */
+#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */
+
+#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */
+#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */
+#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
+#define SD_APP_SEND_SCR 51 /* adtc R1 */
+
+
+#define MMC_RSP_PRESENT (1 << 0)
+#define MMC_RSP_136 (1 << 1) /* 136 bit response */
+#define MMC_RSP_CRC (1 << 2) /* expect valid crc */
+#define MMC_RSP_BUSY (1 << 3) /* card may send busy */
+#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */
+
+#define MMC_CMD_MASK (3 << 5) /* non-SPI command type */
+#define MMC_CMD_AC (0 << 5)
+#define MMC_CMD_ADTC (1 << 5)
+#define MMC_CMD_BC (2 << 5)
+#define MMC_CMD_BCR (3 << 5)
+
+#define MMC_RSP_SPI_S1 (1 << 7) /* one status byte */
+#define MMC_RSP_SPI_S2 (1 << 8) /* second byte */
+#define MMC_RSP_SPI_B4 (1 << 9) /* four data bytes */
+#define MMC_RSP_SPI_BUSY (1 << 10) /* card may send busy */
+
+/*
+ * These are the native response types, and correspond to valid bit
+ * patterns of the above flags. One additional valid pattern
+ * is all zeros, which means we don't expect a response.
+ */
+#define MMC_RSP_NONE (0)
+#define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
+#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
+#define MMC_RSP_R3 (MMC_RSP_PRESENT)
+#define MMC_RSP_R4 (MMC_RSP_PRESENT)
+#define MMC_RSP_R5 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+
+#define mmc_resp_type(f) ((f) & (MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC |\
+ MMC_RSP_BUSY | MMC_RSP_OPCODE))
+#define mmc_cmd_type(f) ((f) & MMC_CMD_MASK)
+
+/*
+ * These are the SPI response types for MMC, SD, and SDIO cards.
+ * Commands return R1, with maybe more info. Zero is an error type;
+ * callers must always provide the appropriate MMC_RSP_SPI_Rx flags.
+ */
+#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1)
+#define MMC_RSP_SPI_R1B (MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY)
+#define MMC_RSP_SPI_R2 (MMC_RSP_SPI_S1|MMC_RSP_SPI_S2)
+#define MMC_RSP_SPI_R3 (MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
+#define MMC_RSP_SPI_R4 (MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
+#define MMC_RSP_SPI_R5 (MMC_RSP_SPI_S1|MMC_RSP_SPI_S2)
+#define MMC_RSP_SPI_R7 (MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
+
+#define MMC_DATA_WRITE (1 << 8)
+#define MMC_DATA_READ (1 << 9)
+#define MMC_DATA_STREAM (1 << 10)
+
+struct sd_cid {
+ char pnm_0; /* product name */
+ char oid_1; /* OEM/application ID */
+ char oid_0;
+ uint8_t mid; /* manufacturer ID */
+ char pnm_4;
+ char pnm_3;
+ char pnm_2;
+ char pnm_1;
+ uint8_t psn_2; /* product serial number */
+ uint8_t psn_1;
+ uint8_t psn_0; /* MSB */
+ uint8_t prv; /* product revision */
+ uint8_t crc; /* CRC7 checksum, b0 is unused and set to 1 */
+ uint8_t mdt_1; /* manufacturing date, LSB, RRRRyyyy yyyymmmm */
+ uint8_t mdt_0; /* MSB */
+ uint8_t psn_3; /* LSB */
+};
+
+enum card_type {
+ CARDTYPE_NONE = 0,
+ CARDTYPE_MMC,
+ CARDTYPE_SD,
+ CARDTYPE_SD20,
+ CARDTYPE_SDHC
+};
+
+
+#endif /* __GLAMO_MMC_H__ */
diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c
index 9546729..471d884 100644
--- a/common/cmd_bootm.c
+++ b/common/cmd_bootm.c
@@ -43,6 +43,8 @@
#include <ft_build.h>
#endif
+void mmc_depower(void);
+
DECLARE_GLOBAL_DATA_PTR;
/*cmd_boot.c*/
@@ -297,6 +299,10 @@ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
}
show_boot_progress (6);
+#ifdef CONFIG_DEPOWER_MMC_ON_BOOT
+ mmc_depower();
+#endif
+
/*
* We have reached the point of no return: we are going to
* overwrite all exception vector code, so we cannot easily
diff --git a/cpu/arm920t/s3c24x0/mmc.c b/cpu/arm920t/s3c24x0/mmc.c
index d056538..d81ba8c 100644
--- a/cpu/arm920t/s3c24x0/mmc.c
+++ b/cpu/arm920t/s3c24x0/mmc.c
@@ -31,7 +31,7 @@
#include <part.h>
#include <fat.h>
-#ifdef CONFIG_MMC
+#if defined(CONFIG_MMC) && defined(CONFIG_MMC_S3C)
#define CONFIG_MMC_WIDE
@@ -536,4 +536,4 @@ mmc2info(ulong addr)
return 0;
}
-#endif /* CONFIG_MMC */
+#endif /* defined(CONFIG_MMC) && defined(CONFIG_MMC_S3C) */
diff --git a/include/configs/neo1973_gta01.h b/include/configs/neo1973_gta01.h
index a9ac6e9..d94a036 100644
--- a/include/configs/neo1973_gta01.h
+++ b/include/configs/neo1973_gta01.h
@@ -218,6 +218,7 @@
#define CFG_MAX_NAND_DEVICE 1
#define CONFIG_MMC 1
+#define CONFIG_MMC_S3C 1
#define CFG_MMC_BASE 0xff000000
/* EXT2 driver */
diff --git a/include/configs/neo1973_gta02.h b/include/configs/neo1973_gta02.h
index 04158f2..adc3d32 100644
--- a/include/configs/neo1973_gta02.h
+++ b/include/configs/neo1973_gta02.h
@@ -218,8 +218,11 @@
#define CFG_NAND_BASE 0x4e000000
#define CFG_MAX_NAND_DEVICE 1
-#define CONFIG_MMC 1
+#define CONFIG_MMC 1
+#define CONFIG_MMC_WIDE 1
+#define CONFIG_MMC_GLAMO 1
#define CFG_MMC_BASE 0xff000000
+#define CONFIG_DEPOWER_MMC_ON_BOOT 1
/* EXT2 driver */
#define CONFIG_EXT2 1