diff mbox

[U-Boot,v3,1/1] ARM: kirkwood: add mvsdio driver

Message ID 1406045573-9258-1-git-send-email-drEagle@doukki.net
State Superseded
Delegated to: Pantelis Antoniou
Headers show

Commit Message

DrEagle July 22, 2014, 4:12 p.m. UTC
This patch add Marvell kirkwood MVSDIO/MMC driver and enable it for Sheevaplugs
and OpenRD boards.

Signed-off-by: Gerald Kerma <drEagle@doukki.net>
---

Changes in v3:
- Add MVSDIO_TWEAK_NOSDHS quirk
- Minor clean

Changes in v2:
- Fix some typo and missing lines from patch import

 arch/arm/cpu/arm926ejs/kirkwood/cpu.c          |  58 ++++
 arch/arm/include/asm/arch-kirkwood/config.h    |   2 +
 arch/arm/include/asm/arch-kirkwood/cpu.h       |   2 +
 arch/arm/include/asm/arch-kirkwood/kirkwood.h  |  24 ++
 arch/arm/include/asm/arch-kirkwood/kw88f6282.h |  33 ++
 board/Marvell/openrd/openrd.c                  |  11 +
 board/Marvell/sheevaplug/sheevaplug.c          |  11 +
 drivers/mmc/Makefile                           |   1 +
 drivers/mmc/mrvl_mmc.c                         | 461 +++++++++++++++++++++++++
 include/configs/openrd.h                       |   8 +
 include/configs/sheevaplug.h                   |  19 +-
 include/mrvl_mmc.h                             | 290 ++++++++++++++++
 12 files changed, 918 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-kirkwood/kw88f6282.h
 create mode 100644 drivers/mmc/mrvl_mmc.c
 create mode 100644 include/mrvl_mmc.h

Comments

Stefan Roese July 24, 2014, 4:47 p.m. UTC | #1
Hi Gerald!

Sorry for the late reply. Please find some comments below.

On 22.07.2014 18:12, Gerald Kerma wrote:
> This patch add Marvell kirkwood MVSDIO/MMC driver and enable it for Sheevaplugs
> and OpenRD boards.
>
> Signed-off-by: Gerald Kerma <drEagle@doukki.net>
> ---
>
> Changes in v3:
> - Add MVSDIO_TWEAK_NOSDHS quirk
> - Minor clean
>
> Changes in v2:
> - Fix some typo and missing lines from patch import

<snip>

>
>   arch/arm/cpu/arm926ejs/kirkwood/cpu.c          |  58 ++++
>   arch/arm/include/asm/arch-kirkwood/config.h    |   2 +
>   arch/arm/include/asm/arch-kirkwood/cpu.h       |   2 +
>   arch/arm/include/asm/arch-kirkwood/kirkwood.h  |  24 ++
>   arch/arm/include/asm/arch-kirkwood/kw88f6282.h |  33 ++
>   board/Marvell/openrd/openrd.c                  |  11 +
>   board/Marvell/sheevaplug/sheevaplug.c          |  11 +
>   drivers/mmc/Makefile                           |   1 +
>   drivers/mmc/mrvl_mmc.c                         | 461 +++++++++++++++++++++++++
>   include/configs/openrd.h                       |   8 +
>   include/configs/sheevaplug.h                   |  19 +-
>   include/mrvl_mmc.h                             | 290 ++++++++++++++++
>   12 files changed, 918 insertions(+), 2 deletions(-)
>   create mode 100644 arch/arm/include/asm/arch-kirkwood/kw88f6282.h
>   create mode 100644 drivers/mmc/mrvl_mmc.c
>   create mode 100644 include/mrvl_mmc.h
>
> diff --git a/arch/arm/cpu/arm926ejs/kirkwood/cpu.c b/arch/arm/cpu/arm926ejs/kirkwood/cpu.c
> index d4711c0..a6e18ff 100644
> --- a/arch/arm/cpu/arm926ejs/kirkwood/cpu.c
> +++ b/arch/arm/cpu/arm926ejs/kirkwood/cpu.c
> @@ -334,6 +334,64 @@ int arch_cpu_init(void)
>   }
>   #endif /* CONFIG_ARCH_CPU_INIT */
>
> +/*****************************************************************************
> + * General
> + ****************************************************************************/
> +#if defined(CONFIG_ARCH_DEV_ID)
> +
> +void kirkwood_pcie_id(u32 *dev, u32 *rev)
> +{
> +	*dev = (readl(KW_REG_PCIE_DEVID) >> 16) & 0xffff;
> +	*rev = readl(KW_REG_PCIE_REVID) & 0xff;
> +}
> +
> +/*
> + * Identify device ID and revision.
> + */
> +char *kirkwood_id(void)
> +{
> +	u32 dev, rev;
> +
> +	kirkwood_pcie_id(&dev, &rev);
> +
> +	if (dev == MV88F6281_DEV_ID) {
> +		if (rev == MV88F6281_REV_Z0)
> +			return "MV88F6281-Z0";
> +		else if (rev == MV88F6281_REV_A0)
> +			return "MV88F6281-A0";
> +		else if (rev == MV88F6281_REV_A1)
> +			return "MV88F6281-A1";
> +		else
> +			return "MV88F6281-Rev-Unsupported";
> +	} else if (dev == MV88F6192_DEV_ID) {
> +		if (rev == MV88F6192_REV_Z0)
> +			return "MV88F6192-Z0";
> +		else if (rev == MV88F6192_REV_A0)
> +			return "MV88F6192-A0";
> +		else if (rev == MV88F6192_REV_A1)
> +			return "MV88F6192-A1";
> +		else
> +			return "MV88F6192-Rev-Unsupported";
> +	} else if (dev == MV88F6180_DEV_ID) {
> +		if (rev == MV88F6180_REV_A0)
> +			return "MV88F6180-Rev-A0";
> +		else if (rev == MV88F6180_REV_A1)
> +			return "MV88F6180-Rev-A1";
> +		else
> +			return "MV88F6180-Rev-Unsupported";
> +	} else if (dev == MV88F6282_DEV_ID) {
> +		if (rev == MV88F6282_REV_A0)
> +			return "MV88F6282-Rev-A0";
> +		else if (rev == MV88F6282_REV_A1)
> +			return "MV88F6282-Rev-A1";
> +		else
> +			return "MV88F6282-Rev-Unsupported";
> +	} else {
> +		return "Device-Unknown";
> +	}
> +}
> +#endif /* CONFIG_ARCH_DEV_ID */

Is this kirkwood_id() function really needed for your MMC driver? This 
looks a big unrelated to me. Even though it might be useful to print the 
SoC revision / version upon bootup. But this should be moved to a 
seperate patch instead.

> +
>   /*
>    * SOC specific misc init
>    */
> diff --git a/arch/arm/include/asm/arch-kirkwood/config.h b/arch/arm/include/asm/arch-kirkwood/config.h
> index 7a688e4..a0563a3 100644
> --- a/arch/arm/include/asm/arch-kirkwood/config.h
> +++ b/arch/arm/include/asm/arch-kirkwood/config.h
> @@ -19,6 +19,8 @@
>   #include <asm/arch/kw88f6281.h>
>   #elif defined (CONFIG_KW88F6192)
>   #include <asm/arch/kw88f6192.h>
> +#elif defined(CONFIG_KW88F6182)
> +#include <asm/arch/kw88f6182.h>
>   #else
>   #error "SOC Name not defined"
>   #endif /* CONFIG_KW88F6281 */
> diff --git a/arch/arm/include/asm/arch-kirkwood/cpu.h b/arch/arm/include/asm/arch-kirkwood/cpu.h
> index 97daa40..e7b6448 100644
> --- a/arch/arm/include/asm/arch-kirkwood/cpu.h
> +++ b/arch/arm/include/asm/arch-kirkwood/cpu.h
> @@ -151,5 +151,7 @@ int kw_config_mpp(unsigned int mpp0_7, unsigned int mpp8_15,
>   		unsigned int mpp32_39, unsigned int mpp40_47,
>   		unsigned int mpp48_55);
>   unsigned int kw_winctrl_calcsize(unsigned int sizeval);
> +void kirkwood_pcie_id(u32 *dev, u32 *rev);
> +char *kirkwood_id(void);
>   #endif /* __ASSEMBLY__ */
>   #endif /* _KWCPU_H */
> diff --git a/arch/arm/include/asm/arch-kirkwood/kirkwood.h b/arch/arm/include/asm/arch-kirkwood/kirkwood.h
> index bc207f5..489517f 100644
> --- a/arch/arm/include/asm/arch-kirkwood/kirkwood.h
> +++ b/arch/arm/include/asm/arch-kirkwood/kirkwood.h
> @@ -39,6 +39,7 @@
>   #define KW_EGIGA0_BASE			(KW_REGISTER(0x72000))
>   #define KW_EGIGA1_BASE			(KW_REGISTER(0x76000))
>   #define KW_SATA_BASE			(KW_REGISTER(0x80000))
> +#define KW_SDIO_BASE			(KW_REGISTER(0x90000))
>
>   /* Kirkwood Sata controller has two ports */
>   #define KW_SATA_PORT0_OFFSET		0x2000
> @@ -61,10 +62,33 @@
>   #define MVCPU_WIN_ENABLE	KWCPU_WIN_ENABLE
>   #define MVCPU_WIN_DISABLE	KWCPU_WIN_DISABLE
>
> +/*
> + * Supported devices and revisions.
> + */
> +#define MV88F6281_DEV_ID	0x6281
> +#define MV88F6281_REV_Z0	0
> +#define MV88F6281_REV_A0	2
> +#define MV88F6281_REV_A1	3
> +
> +#define MV88F6192_DEV_ID	0x6192
> +#define MV88F6192_REV_Z0	0
> +#define MV88F6192_REV_A0	2
> +#define MV88F6192_REV_A1	3
> +
> +#define MV88F6180_DEV_ID	0x6180
> +#define MV88F6180_REV_A0	2
> +#define MV88F6180_REV_A1	3
> +
> +#define MV88F6282_DEV_ID	0x6282
> +#define MV88F6282_REV_A0	0
> +#define MV88F6282_REV_A1	1
> +
>   #if defined (CONFIG_KW88F6281)
>   #include <asm/arch/kw88f6281.h>
>   #elif defined (CONFIG_KW88F6192)
>   #include <asm/arch/kw88f6192.h>
> +#elif defined(CONFIG_KW88F6182)
> +#include <asm/arch/kw88f6182.h>
>   #else
>   #error "SOC Name not defined"
>   #endif /* CONFIG_KW88F6281 */
> diff --git a/arch/arm/include/asm/arch-kirkwood/kw88f6282.h b/arch/arm/include/asm/arch-kirkwood/kw88f6282.h
> new file mode 100644
> index 0000000..5310da2
> --- /dev/null
> +++ b/arch/arm/include/asm/arch-kirkwood/kw88f6282.h
> @@ -0,0 +1,33 @@
> +/*
> + * (C) Copyright 2009
> + * Marvell Semiconductor <www.marvell.com>
> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com>
> + *
> + * Header file for Feroceon CPU core 88FR131 Based KW88F6281 SOC.
> + *
> + * 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.
> + */

Please use the SPDX license identifiers instead. And fix this globally.

> +
> +#ifndef _ASM_ARCH_KW88F6281_H
> +#define _ASM_ARCH_KW88F6281_H
> +
> +/* SOC specific definitions */
> +#define KW88F6281_REGS_PHYS_BASE	0xf1000000
> +#define KW_REGS_PHY_BASE		KW88F6281_REGS_PHYS_BASE
> +
> +/* TCLK Core Clock definition */
> +#ifndef CONFIG_SYS_TCLK
> +#define CONFIG_SYS_TCLK	200000000 /* 200MHz */
> +#endif
> +
> diff --git a/board/Marvell/openrd/openrd.c b/board/Marvell/openrd/openrd.c
> index a005a2f..a0fb18a 100644
> --- a/board/Marvell/openrd/openrd.c
> +++ b/board/Marvell/openrd/openrd.c
> @@ -17,6 +17,9 @@
>   #include <asm/arch/kirkwood.h>
>   #include <asm/arch/mpp.h>
>   #include "openrd.h"
> +#ifdef CONFIG_MRVL_MMC
> +#include <mrvl_mmc.h>
> +#endif /* CONFIG_MRVL_MMC */
>
>   DECLARE_GLOBAL_DATA_PTR;
>
> @@ -159,3 +162,11 @@ void reset_phy(void)
>   #endif
>   }
>   #endif /* CONFIG_RESET_PHY_R */
> +
> +#ifdef CONFIG_MRVL_MMC
> +int board_mmc_init(bd_t *bis)
> +{
> +	mrvl_mmc_init(bis);
> +	return 0;
> +}
> +#endif /* CONFIG_MRVL_MMC */
> diff --git a/board/Marvell/sheevaplug/sheevaplug.c b/board/Marvell/sheevaplug/sheevaplug.c
> index 87e49f4..20096d5 100644
> --- a/board/Marvell/sheevaplug/sheevaplug.c
> +++ b/board/Marvell/sheevaplug/sheevaplug.c
> @@ -12,6 +12,9 @@
>   #include <asm/arch/kirkwood.h>
>   #include <asm/arch/mpp.h>
>   #include "sheevaplug.h"
> +#ifdef CONFIG_MRVL_MMC
> +#include <mrvl_mmc.h>
> +#endif /* CONFIG_MRVL_MMC */
>
>   DECLARE_GLOBAL_DATA_PTR;
>
> @@ -131,3 +134,11 @@ void reset_phy(void)
>   	printf("88E1116 Initialized on %s\n", name);
>   }
>   #endif /* CONFIG_RESET_PHY_R */
> +
> +#ifdef CONFIG_MRVL_MMC
> +int board_mmc_init(bd_t *bis)
> +{
> +	mrvl_mmc_init(bis);
> +	return 0;
> +}
> +#endif /* CONFIG_MRVL_MMC */
> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
> index 931922b..74b64d6 100644
> --- a/drivers/mmc/Makefile
> +++ b/drivers/mmc/Makefile
> @@ -35,3 +35,4 @@ obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
>   else
>   obj-$(CONFIG_GENERIC_MMC) += mmc_write.o
>   endif
> +obj-$(CONFIG_MRVL_MMC) += mrvl_mmc.o
> diff --git a/drivers/mmc/mrvl_mmc.c b/drivers/mmc/mrvl_mmc.c
> new file mode 100644
> index 0000000..acc16c4
> --- /dev/null
> +++ b/drivers/mmc/mrvl_mmc.c

I assume that "mrvl" stands for Marvell, right? We should not add 
another abbreviation for Marvell. How is this driver called in Linux? 
Perhaps we should use "mvebu" as done for other Marvell perepherals that 
are shared by Orion, Kirkwood, Armada etc.

> @@ -0,0 +1,461 @@
> +/*
> + * Driver for Marvell SDIO/MMC controller
> + *
> + * (C) Copyright 2012
> + * Marvell Semiconductor <www.marvell.com>
> + * Written-by: Gérald Kerma <dreagle@doukki.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.
> + */
> +
> +#include <common.h>
> +#include <malloc.h>
> +#include <part.h>
> +#include <mmc.h>
> +#include <asm/io.h>
> +#include <asm/arch/cpu.h>
> +#include <asm/arch/kirkwood.h>
> +
> +#include <mrvl_mmc.h>
> +
> +#define DRIVER_NAME	"MVSDIO"
> +
> +#define mvsd_write(offs, val) writel(val, CONFIG_SYS_MMC_BASE + (offs))
> +#define mvsd_read(offs) readl(CONFIG_SYS_MMC_BASE + (offs))
> +
> +#define MRVL_MMC_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
> +
> +static int mrvl_mmc_setup_data(struct mmc_data *data)
> +{
> +	u32 ctrl_reg;
> +
> +#ifdef DEBUG
> +	printf("%s, data %s : blocks=%d blksz=%d\n", DRIVER_NAME,
> +	       (data->flags & MMC_DATA_READ) ? "read" : "write",
> +	       data->blocks, data->blocksize);
> +#endif
> +
> +	/* default to maximum timeout */
> +	ctrl_reg = mvsd_read(SDIO_HOST_CTRL);
> +	ctrl_reg |= SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX);
> +	mvsd_write(SDIO_HOST_CTRL, ctrl_reg);
> +
> +	if (data->flags & MMC_DATA_READ) {
> +		mvsd_write(SDIO_SYS_ADDR_LOW, (u32)data->dest & 0xffff);
> +		mvsd_write(SDIO_SYS_ADDR_HI, (u32)data->dest >> 16);
> +	} else {
> +		mvsd_write(SDIO_SYS_ADDR_LOW, (u32)data->src & 0xffff);
> +		mvsd_write(SDIO_SYS_ADDR_HI, (u32)data->src >> 16);
> +	}
> +
> +	mvsd_write(SDIO_BLK_COUNT, data->blocks);
> +	mvsd_write(SDIO_BLK_SIZE, data->blocksize);
> +
> +	return 0;
> +}
> +
> +static int mrvl_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
> +			      struct mmc_data *data)
> +{
> +	int	timeout = 10;
> +	ushort waittype = 0;
> +	ushort resptype = 0;
> +	ushort xfertype = 0;
> +	ushort resp_indx = 0;
> +
> +#ifdef CONFIG_MMC_DEBUG
> +	printf("cmdidx [0x%x] resp_type[0x%x] cmdarg[0x%x]\n",
> +	       cmd->cmdidx, cmd->resp_type, cmd->cmdarg);
> +
> +	printf("%s: cmd %d (hw state 0x%04x)\n", DRIVER_NAME,
> +	       cmd->cmdidx, mvsd_read(SDIO_HW_STATE));
> +#endif

Hmmm. You seem to have some "#if DEBUG" and "#ifdef CONFIG_MMC_DEBUG" 
lines in this driver. I suggest to move to the common debug() function 
instead.

> +
> +	udelay(10*1000);

This should at least generate a checkpatch warning. Did you run this 
through checkpatch? If not, please do.

> +
> +	/* Checking if card is busy */
> +	while ((mvsd_read(SDIO_HW_STATE) & CARD_BUSY)) {
> +		if (timeout == 0) {
> +			printf("%s: card busy!\n", DRIVER_NAME);
> +			return -1;
> +		}
> +		timeout--;
> +		udelay(1000);
> +	}
> +
> +	/* Set up for a data transfer if we have one */
> +	if (data) {
> +		int err = mrvl_mmc_setup_data(data);
> +
> +		if (err)
> +			return err;
> +	}
> +
> +	resptype = SDIO_CMD_INDEX(cmd->cmdidx);
> +
> +	/* Analyzing resptype/xfertype/waittype for the command */
> +	if (cmd->resp_type & MMC_RSP_BUSY)
> +		resptype |= SDIO_CMD_RSP_48BUSY;
> +	else if (cmd->resp_type & MMC_RSP_136)
> +		resptype |= SDIO_CMD_RSP_136;
> +	else if (cmd->resp_type & MMC_RSP_PRESENT)
> +		resptype |= SDIO_CMD_RSP_48;
> +	else
> +		resptype |= SDIO_CMD_RSP_NONE;
> +
> +	if (cmd->resp_type & MMC_RSP_CRC)
> +		resptype |= SDIO_CMD_CHECK_CMDCRC;
> +
> +	if (cmd->resp_type & MMC_RSP_OPCODE)
> +		resptype |= SDIO_CMD_INDX_CHECK;
> +
> +	if (cmd->resp_type & MMC_RSP_PRESENT) {
> +		resptype |= SDIO_UNEXPECTED_RESP;
> +		waittype |= SDIO_NOR_UNEXP_RSP;
> +	}
> +
> +	if (data) {
> +		resptype |= SDIO_CMD_DATA_PRESENT | SDIO_CMD_CHECK_DATACRC16;
> +		xfertype |= SDIO_XFER_MODE_HW_WR_DATA_EN;
> +		if (data->flags & MMC_DATA_READ) {
> +			xfertype |= SDIO_XFER_MODE_TO_HOST;
> +			waittype = SDIO_NOR_DMA_INI;
> +		} else {
> +			waittype |= SDIO_NOR_XFER_DONE;
> +		}
> +	} else {
> +		waittype |= SDIO_NOR_CMD_DONE;
> +	}
> +
> +	/* Setting cmd arguments */
> +	mvsd_write(SDIO_ARG_LOW, cmd->cmdarg & 0xffff);
> +	mvsd_write(SDIO_ARG_HI, cmd->cmdarg >> 16);
> +
> +	/* Setting Xfer mode */
> +	mvsd_write(SDIO_XFER_MODE, xfertype);
> +
> +	mvsd_write(SDIO_NOR_INTR_STATUS, ~SDIO_NOR_CARD_INT);
> +	mvsd_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK);
> +
> +	/* Sending command */
> +	mvsd_write(SDIO_CMD, resptype);
> +/*
> +	mvsd_write(SDIO_CMD, MRVL_MMC_MAKE_CMD(cmd->cmdidx, resptype));
> +*/

No dead code please.

> +
> +	mvsd_write(SDIO_NOR_INTR_EN, SDIO_POLL_MASK);
> +	mvsd_write(SDIO_ERR_INTR_EN, SDIO_POLL_MASK);
> +
> +	/* Waiting for completion */
> +	timeout = 1000000;
> +
> +	while (!((mvsd_read(SDIO_NOR_INTR_STATUS)) & waittype)) {
> +		if (mvsd_read(SDIO_NOR_INTR_STATUS) & SDIO_NOR_ERROR) {
> +#ifdef DEBUG
> +			printf("%s: error! cmdidx : %d, err reg: %04x\n",
> +			       DRIVER_NAME, cmd->cmdidx,
> +			       mvsd_read(SDIO_ERR_INTR_STATUS));
> +#endif
> +			if (mvsd_read(SDIO_ERR_INTR_STATUS) &
> +				(SDIO_ERR_CMD_TIMEOUT | SDIO_ERR_DATA_TIMEOUT))
> +				return TIMEOUT;
> +			return COMM_ERR;
> +		}
> +
> +		timeout--;
> +		udelay(1);
> +		if (timeout <= 0) {
> +			printf("%s: command timed out\n", DRIVER_NAME);
> +			return TIMEOUT;
> +		}
> +	}
> +
> +	/* Handling response */
> +	if (cmd->resp_type & MMC_RSP_136) {
> +		uint response[8];
> +
> +		for (resp_indx = 0; resp_indx < 8; resp_indx++)
> +			response[resp_indx] = mvsd_read(SDIO_RSP(resp_indx));
> +
> +		cmd->response[0] =	((response[0] & 0x03ff) << 22) |
> +					((response[1] & 0xffff) << 6) |
> +					((response[2] & 0xfc00) >> 10);
> +		cmd->response[1] =	((response[2] & 0x03ff) << 22) |
> +					((response[3] & 0xffff) << 6) |
> +					((response[4] & 0xfc00) >> 10);
> +		cmd->response[2] =	((response[4] & 0x03ff) << 22) |
> +					((response[5] & 0xffff) << 6) |
> +					((response[6] & 0xfc00) >> 10);
> +		cmd->response[3] =	((response[6] & 0x03ff) << 22) |
> +					((response[7] & 0x3fff) << 8);
> +	} else if (cmd->resp_type & MMC_RSP_PRESENT) {
> +		uint response[3];
> +
> +		for (resp_indx = 0; resp_indx < 3; resp_indx++)
> +			response[resp_indx] = mvsd_read(SDIO_RSP(resp_indx));
> +
> +		cmd->response[0] =	((response[2] & 0x003f) << (8 - 8)) |
> +					((response[1] & 0xffff) << (14 - 8)) |
> +					((response[0] & 0x03ff) << (30 - 8));
> +		cmd->response[1] =	((response[0] & 0xfc00) >> 10);
> +		cmd->response[2] =	0;
> +		cmd->response[3] =	0;
> +	}
> +
> +#ifdef CONFIG_MMC_DEBUG
> +	printf("%s: resp[0x%x] ", DRIVER_NAME, cmd->resp_type);
> +	printf("[0x%x] ", cmd->response[0]);
> +	printf("[0x%x] ", cmd->response[1]);
> +	printf("[0x%x] ", cmd->response[2]);
> +	printf("[0x%x] ", cmd->response[3]);
> +	printf("\n");
> +#endif
> +
> +	return 0;
> +}
> +
> +static void mvsd_power_up(void)
> +{
> +#ifdef DEBUG
> +	printf("%s: power up\n", DRIVER_NAME);
> +#endif
> +	/* disable interrupts */
> +	mvsd_write(SDIO_NOR_INTR_EN, 0);
> +	mvsd_write(SDIO_ERR_INTR_EN, 0);
> +
> +	/* SW reset */
> +	mvsd_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW);
> +
> +	mvsd_write(SDIO_XFER_MODE, 0);
> +
> +	/* enable status */
> +	mvsd_write(SDIO_NOR_STATUS_EN, SDIO_POLL_MASK);
> +	mvsd_write(SDIO_ERR_STATUS_EN, SDIO_POLL_MASK);
> +
> +	/* enable interrupts status */
> +	mvsd_write(SDIO_NOR_INTR_STATUS, SDIO_POLL_MASK);
> +	mvsd_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK);
> +}
> +
> +static void mvsd_power_down(void)
> +{
> +#ifdef DEBUG
> +	printf("%s: power down\n", DRIVER_NAME);
> +#endif
> +	/* disable interrupts */
> +	mvsd_write(SDIO_NOR_INTR_EN, 0);
> +	mvsd_write(SDIO_ERR_INTR_EN, 0);
> +
> +	/* SW reset */
> +	mvsd_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW);
> +
> +	mvsd_write(SDIO_XFER_MODE, SDIO_XFER_MODE_STOP_CLK);
> +
> +	/* disable status */
> +	mvsd_write(SDIO_NOR_STATUS_EN, 0);
> +	mvsd_write(SDIO_ERR_STATUS_EN, 0);
> +
> +	/* enable interrupts status */
> +	mvsd_write(SDIO_NOR_INTR_STATUS, SDIO_POLL_MASK);
> +	mvsd_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK);
> +}
> +
> +static u32 mrvl_mmc_get_base_clock(void)
> +{
> +	u32 devid;
> +	u32 revid;
> +
> +	if (0) {
> +		kirkwood_pcie_id(&devid, &revid);
> +
> +#ifdef DEBUG
> +		printf("%s: board devid[0x%x] devid[0x%x]\n",
> +		       DRIVER_NAME, devid, revid);
> +#endif
> +
> +		if (revid == 0 && devid != MV88F6282_DEV_ID) {
> +			/* catch all Kirkwood Z0's */
> +#ifdef DEBUG
> +			printf("%s: Kirkwood Z0's base clock = %d\n",
> +			       DRIVER_NAME, MRVL_MMC_BASE_FAST_CLK_100);
> +#endif
> +			return MRVL_MMC_BASE_FAST_CLK_100;
> +		} else {
> +#ifdef DEBUG
> +			printf("%s: base clock = %d\n",
> +			       DRIVER_NAME, MRVL_MMC_BASE_FAST_CLK_200);
> +#endif

This really look ugly. Please switch to debug() instead as mentioned above.

Thanks,
Stefan
diff mbox

Patch

diff --git a/arch/arm/cpu/arm926ejs/kirkwood/cpu.c b/arch/arm/cpu/arm926ejs/kirkwood/cpu.c
index d4711c0..a6e18ff 100644
--- a/arch/arm/cpu/arm926ejs/kirkwood/cpu.c
+++ b/arch/arm/cpu/arm926ejs/kirkwood/cpu.c
@@ -334,6 +334,64 @@  int arch_cpu_init(void)
 }
 #endif /* CONFIG_ARCH_CPU_INIT */
 
+/*****************************************************************************
+ * General
+ ****************************************************************************/
+#if defined(CONFIG_ARCH_DEV_ID)
+
+void kirkwood_pcie_id(u32 *dev, u32 *rev)
+{
+	*dev = (readl(KW_REG_PCIE_DEVID) >> 16) & 0xffff;
+	*rev = readl(KW_REG_PCIE_REVID) & 0xff;
+}
+
+/*
+ * Identify device ID and revision.
+ */
+char *kirkwood_id(void)
+{
+	u32 dev, rev;
+
+	kirkwood_pcie_id(&dev, &rev);
+
+	if (dev == MV88F6281_DEV_ID) {
+		if (rev == MV88F6281_REV_Z0)
+			return "MV88F6281-Z0";
+		else if (rev == MV88F6281_REV_A0)
+			return "MV88F6281-A0";
+		else if (rev == MV88F6281_REV_A1)
+			return "MV88F6281-A1";
+		else
+			return "MV88F6281-Rev-Unsupported";
+	} else if (dev == MV88F6192_DEV_ID) {
+		if (rev == MV88F6192_REV_Z0)
+			return "MV88F6192-Z0";
+		else if (rev == MV88F6192_REV_A0)
+			return "MV88F6192-A0";
+		else if (rev == MV88F6192_REV_A1)
+			return "MV88F6192-A1";
+		else
+			return "MV88F6192-Rev-Unsupported";
+	} else if (dev == MV88F6180_DEV_ID) {
+		if (rev == MV88F6180_REV_A0)
+			return "MV88F6180-Rev-A0";
+		else if (rev == MV88F6180_REV_A1)
+			return "MV88F6180-Rev-A1";
+		else
+			return "MV88F6180-Rev-Unsupported";
+	} else if (dev == MV88F6282_DEV_ID) {
+		if (rev == MV88F6282_REV_A0)
+			return "MV88F6282-Rev-A0";
+		else if (rev == MV88F6282_REV_A1)
+			return "MV88F6282-Rev-A1";
+		else
+			return "MV88F6282-Rev-Unsupported";
+	} else {
+		return "Device-Unknown";
+	}
+}
+#endif /* CONFIG_ARCH_DEV_ID */
+
 /*
  * SOC specific misc init
  */
diff --git a/arch/arm/include/asm/arch-kirkwood/config.h b/arch/arm/include/asm/arch-kirkwood/config.h
index 7a688e4..a0563a3 100644
--- a/arch/arm/include/asm/arch-kirkwood/config.h
+++ b/arch/arm/include/asm/arch-kirkwood/config.h
@@ -19,6 +19,8 @@ 
 #include <asm/arch/kw88f6281.h>
 #elif defined (CONFIG_KW88F6192)
 #include <asm/arch/kw88f6192.h>
+#elif defined(CONFIG_KW88F6182)
+#include <asm/arch/kw88f6182.h>
 #else
 #error "SOC Name not defined"
 #endif /* CONFIG_KW88F6281 */
diff --git a/arch/arm/include/asm/arch-kirkwood/cpu.h b/arch/arm/include/asm/arch-kirkwood/cpu.h
index 97daa40..e7b6448 100644
--- a/arch/arm/include/asm/arch-kirkwood/cpu.h
+++ b/arch/arm/include/asm/arch-kirkwood/cpu.h
@@ -151,5 +151,7 @@  int kw_config_mpp(unsigned int mpp0_7, unsigned int mpp8_15,
 		unsigned int mpp32_39, unsigned int mpp40_47,
 		unsigned int mpp48_55);
 unsigned int kw_winctrl_calcsize(unsigned int sizeval);
+void kirkwood_pcie_id(u32 *dev, u32 *rev);
+char *kirkwood_id(void);
 #endif /* __ASSEMBLY__ */
 #endif /* _KWCPU_H */
diff --git a/arch/arm/include/asm/arch-kirkwood/kirkwood.h b/arch/arm/include/asm/arch-kirkwood/kirkwood.h
index bc207f5..489517f 100644
--- a/arch/arm/include/asm/arch-kirkwood/kirkwood.h
+++ b/arch/arm/include/asm/arch-kirkwood/kirkwood.h
@@ -39,6 +39,7 @@ 
 #define KW_EGIGA0_BASE			(KW_REGISTER(0x72000))
 #define KW_EGIGA1_BASE			(KW_REGISTER(0x76000))
 #define KW_SATA_BASE			(KW_REGISTER(0x80000))
+#define KW_SDIO_BASE			(KW_REGISTER(0x90000))
 
 /* Kirkwood Sata controller has two ports */
 #define KW_SATA_PORT0_OFFSET		0x2000
@@ -61,10 +62,33 @@ 
 #define MVCPU_WIN_ENABLE	KWCPU_WIN_ENABLE
 #define MVCPU_WIN_DISABLE	KWCPU_WIN_DISABLE
 
+/*
+ * Supported devices and revisions.
+ */
+#define MV88F6281_DEV_ID	0x6281
+#define MV88F6281_REV_Z0	0
+#define MV88F6281_REV_A0	2
+#define MV88F6281_REV_A1	3
+
+#define MV88F6192_DEV_ID	0x6192
+#define MV88F6192_REV_Z0	0
+#define MV88F6192_REV_A0	2
+#define MV88F6192_REV_A1	3
+
+#define MV88F6180_DEV_ID	0x6180
+#define MV88F6180_REV_A0	2
+#define MV88F6180_REV_A1	3
+
+#define MV88F6282_DEV_ID	0x6282
+#define MV88F6282_REV_A0	0
+#define MV88F6282_REV_A1	1
+
 #if defined (CONFIG_KW88F6281)
 #include <asm/arch/kw88f6281.h>
 #elif defined (CONFIG_KW88F6192)
 #include <asm/arch/kw88f6192.h>
+#elif defined(CONFIG_KW88F6182)
+#include <asm/arch/kw88f6182.h>
 #else
 #error "SOC Name not defined"
 #endif /* CONFIG_KW88F6281 */
diff --git a/arch/arm/include/asm/arch-kirkwood/kw88f6282.h b/arch/arm/include/asm/arch-kirkwood/kw88f6282.h
new file mode 100644
index 0000000..5310da2
--- /dev/null
+++ b/arch/arm/include/asm/arch-kirkwood/kw88f6282.h
@@ -0,0 +1,33 @@ 
+/*
+ * (C) Copyright 2009
+ * Marvell Semiconductor <www.marvell.com>
+ * Written-by: Prafulla Wadaskar <prafulla@marvell.com>
+ *
+ * Header file for Feroceon CPU core 88FR131 Based KW88F6281 SOC.
+ *
+ * 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.
+ */
+
+#ifndef _ASM_ARCH_KW88F6281_H
+#define _ASM_ARCH_KW88F6281_H
+
+/* SOC specific definitions */
+#define KW88F6281_REGS_PHYS_BASE	0xf1000000
+#define KW_REGS_PHY_BASE		KW88F6281_REGS_PHYS_BASE
+
+/* TCLK Core Clock definition */
+#ifndef CONFIG_SYS_TCLK
+#define CONFIG_SYS_TCLK	200000000 /* 200MHz */
+#endif
+
diff --git a/board/Marvell/openrd/openrd.c b/board/Marvell/openrd/openrd.c
index a005a2f..a0fb18a 100644
--- a/board/Marvell/openrd/openrd.c
+++ b/board/Marvell/openrd/openrd.c
@@ -17,6 +17,9 @@ 
 #include <asm/arch/kirkwood.h>
 #include <asm/arch/mpp.h>
 #include "openrd.h"
+#ifdef CONFIG_MRVL_MMC
+#include <mrvl_mmc.h>
+#endif /* CONFIG_MRVL_MMC */
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -159,3 +162,11 @@  void reset_phy(void)
 #endif
 }
 #endif /* CONFIG_RESET_PHY_R */
+
+#ifdef CONFIG_MRVL_MMC
+int board_mmc_init(bd_t *bis)
+{
+	mrvl_mmc_init(bis);
+	return 0;
+}
+#endif /* CONFIG_MRVL_MMC */
diff --git a/board/Marvell/sheevaplug/sheevaplug.c b/board/Marvell/sheevaplug/sheevaplug.c
index 87e49f4..20096d5 100644
--- a/board/Marvell/sheevaplug/sheevaplug.c
+++ b/board/Marvell/sheevaplug/sheevaplug.c
@@ -12,6 +12,9 @@ 
 #include <asm/arch/kirkwood.h>
 #include <asm/arch/mpp.h>
 #include "sheevaplug.h"
+#ifdef CONFIG_MRVL_MMC
+#include <mrvl_mmc.h>
+#endif /* CONFIG_MRVL_MMC */
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -131,3 +134,11 @@  void reset_phy(void)
 	printf("88E1116 Initialized on %s\n", name);
 }
 #endif /* CONFIG_RESET_PHY_R */
+
+#ifdef CONFIG_MRVL_MMC
+int board_mmc_init(bd_t *bis)
+{
+	mrvl_mmc_init(bis);
+	return 0;
+}
+#endif /* CONFIG_MRVL_MMC */
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 931922b..74b64d6 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -35,3 +35,4 @@  obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
 else
 obj-$(CONFIG_GENERIC_MMC) += mmc_write.o
 endif
+obj-$(CONFIG_MRVL_MMC) += mrvl_mmc.o
diff --git a/drivers/mmc/mrvl_mmc.c b/drivers/mmc/mrvl_mmc.c
new file mode 100644
index 0000000..acc16c4
--- /dev/null
+++ b/drivers/mmc/mrvl_mmc.c
@@ -0,0 +1,461 @@ 
+/*
+ * Driver for Marvell SDIO/MMC controller
+ *
+ * (C) Copyright 2012
+ * Marvell Semiconductor <www.marvell.com>
+ * Written-by: Gérald Kerma <dreagle@doukki.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.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <part.h>
+#include <mmc.h>
+#include <asm/io.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/kirkwood.h>
+
+#include <mrvl_mmc.h>
+
+#define DRIVER_NAME	"MVSDIO"
+
+#define mvsd_write(offs, val) writel(val, CONFIG_SYS_MMC_BASE + (offs))
+#define mvsd_read(offs) readl(CONFIG_SYS_MMC_BASE + (offs))
+
+#define MRVL_MMC_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
+
+static int mrvl_mmc_setup_data(struct mmc_data *data)
+{
+	u32 ctrl_reg;
+
+#ifdef DEBUG
+	printf("%s, data %s : blocks=%d blksz=%d\n", DRIVER_NAME,
+	       (data->flags & MMC_DATA_READ) ? "read" : "write",
+	       data->blocks, data->blocksize);
+#endif
+
+	/* default to maximum timeout */
+	ctrl_reg = mvsd_read(SDIO_HOST_CTRL);
+	ctrl_reg |= SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX);
+	mvsd_write(SDIO_HOST_CTRL, ctrl_reg);
+
+	if (data->flags & MMC_DATA_READ) {
+		mvsd_write(SDIO_SYS_ADDR_LOW, (u32)data->dest & 0xffff);
+		mvsd_write(SDIO_SYS_ADDR_HI, (u32)data->dest >> 16);
+	} else {
+		mvsd_write(SDIO_SYS_ADDR_LOW, (u32)data->src & 0xffff);
+		mvsd_write(SDIO_SYS_ADDR_HI, (u32)data->src >> 16);
+	}
+
+	mvsd_write(SDIO_BLK_COUNT, data->blocks);
+	mvsd_write(SDIO_BLK_SIZE, data->blocksize);
+
+	return 0;
+}
+
+static int mrvl_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+			      struct mmc_data *data)
+{
+	int	timeout = 10;
+	ushort waittype = 0;
+	ushort resptype = 0;
+	ushort xfertype = 0;
+	ushort resp_indx = 0;
+
+#ifdef CONFIG_MMC_DEBUG
+	printf("cmdidx [0x%x] resp_type[0x%x] cmdarg[0x%x]\n",
+	       cmd->cmdidx, cmd->resp_type, cmd->cmdarg);
+
+	printf("%s: cmd %d (hw state 0x%04x)\n", DRIVER_NAME,
+	       cmd->cmdidx, mvsd_read(SDIO_HW_STATE));
+#endif
+
+	udelay(10*1000);
+
+	/* Checking if card is busy */
+	while ((mvsd_read(SDIO_HW_STATE) & CARD_BUSY)) {
+		if (timeout == 0) {
+			printf("%s: card busy!\n", DRIVER_NAME);
+			return -1;
+		}
+		timeout--;
+		udelay(1000);
+	}
+
+	/* Set up for a data transfer if we have one */
+	if (data) {
+		int err = mrvl_mmc_setup_data(data);
+
+		if (err)
+			return err;
+	}
+
+	resptype = SDIO_CMD_INDEX(cmd->cmdidx);
+
+	/* Analyzing resptype/xfertype/waittype for the command */
+	if (cmd->resp_type & MMC_RSP_BUSY)
+		resptype |= SDIO_CMD_RSP_48BUSY;
+	else if (cmd->resp_type & MMC_RSP_136)
+		resptype |= SDIO_CMD_RSP_136;
+	else if (cmd->resp_type & MMC_RSP_PRESENT)
+		resptype |= SDIO_CMD_RSP_48;
+	else
+		resptype |= SDIO_CMD_RSP_NONE;
+
+	if (cmd->resp_type & MMC_RSP_CRC)
+		resptype |= SDIO_CMD_CHECK_CMDCRC;
+
+	if (cmd->resp_type & MMC_RSP_OPCODE)
+		resptype |= SDIO_CMD_INDX_CHECK;
+
+	if (cmd->resp_type & MMC_RSP_PRESENT) {
+		resptype |= SDIO_UNEXPECTED_RESP;
+		waittype |= SDIO_NOR_UNEXP_RSP;
+	}
+
+	if (data) {
+		resptype |= SDIO_CMD_DATA_PRESENT | SDIO_CMD_CHECK_DATACRC16;
+		xfertype |= SDIO_XFER_MODE_HW_WR_DATA_EN;
+		if (data->flags & MMC_DATA_READ) {
+			xfertype |= SDIO_XFER_MODE_TO_HOST;
+			waittype = SDIO_NOR_DMA_INI;
+		} else {
+			waittype |= SDIO_NOR_XFER_DONE;
+		}
+	} else {
+		waittype |= SDIO_NOR_CMD_DONE;
+	}
+
+	/* Setting cmd arguments */
+	mvsd_write(SDIO_ARG_LOW, cmd->cmdarg & 0xffff);
+	mvsd_write(SDIO_ARG_HI, cmd->cmdarg >> 16);
+
+	/* Setting Xfer mode */
+	mvsd_write(SDIO_XFER_MODE, xfertype);
+
+	mvsd_write(SDIO_NOR_INTR_STATUS, ~SDIO_NOR_CARD_INT);
+	mvsd_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK);
+
+	/* Sending command */
+	mvsd_write(SDIO_CMD, resptype);
+/*
+	mvsd_write(SDIO_CMD, MRVL_MMC_MAKE_CMD(cmd->cmdidx, resptype));
+*/
+
+	mvsd_write(SDIO_NOR_INTR_EN, SDIO_POLL_MASK);
+	mvsd_write(SDIO_ERR_INTR_EN, SDIO_POLL_MASK);
+
+	/* Waiting for completion */
+	timeout = 1000000;
+
+	while (!((mvsd_read(SDIO_NOR_INTR_STATUS)) & waittype)) {
+		if (mvsd_read(SDIO_NOR_INTR_STATUS) & SDIO_NOR_ERROR) {
+#ifdef DEBUG
+			printf("%s: error! cmdidx : %d, err reg: %04x\n",
+			       DRIVER_NAME, cmd->cmdidx,
+			       mvsd_read(SDIO_ERR_INTR_STATUS));
+#endif
+			if (mvsd_read(SDIO_ERR_INTR_STATUS) &
+				(SDIO_ERR_CMD_TIMEOUT | SDIO_ERR_DATA_TIMEOUT))
+				return TIMEOUT;
+			return COMM_ERR;
+		}
+
+		timeout--;
+		udelay(1);
+		if (timeout <= 0) {
+			printf("%s: command timed out\n", DRIVER_NAME);
+			return TIMEOUT;
+		}
+	}
+
+	/* Handling response */
+	if (cmd->resp_type & MMC_RSP_136) {
+		uint response[8];
+
+		for (resp_indx = 0; resp_indx < 8; resp_indx++)
+			response[resp_indx] = mvsd_read(SDIO_RSP(resp_indx));
+
+		cmd->response[0] =	((response[0] & 0x03ff) << 22) |
+					((response[1] & 0xffff) << 6) |
+					((response[2] & 0xfc00) >> 10);
+		cmd->response[1] =	((response[2] & 0x03ff) << 22) |
+					((response[3] & 0xffff) << 6) |
+					((response[4] & 0xfc00) >> 10);
+		cmd->response[2] =	((response[4] & 0x03ff) << 22) |
+					((response[5] & 0xffff) << 6) |
+					((response[6] & 0xfc00) >> 10);
+		cmd->response[3] =	((response[6] & 0x03ff) << 22) |
+					((response[7] & 0x3fff) << 8);
+	} else if (cmd->resp_type & MMC_RSP_PRESENT) {
+		uint response[3];
+
+		for (resp_indx = 0; resp_indx < 3; resp_indx++)
+			response[resp_indx] = mvsd_read(SDIO_RSP(resp_indx));
+
+		cmd->response[0] =	((response[2] & 0x003f) << (8 - 8)) |
+					((response[1] & 0xffff) << (14 - 8)) |
+					((response[0] & 0x03ff) << (30 - 8));
+		cmd->response[1] =	((response[0] & 0xfc00) >> 10);
+		cmd->response[2] =	0;
+		cmd->response[3] =	0;
+	}
+
+#ifdef CONFIG_MMC_DEBUG
+	printf("%s: resp[0x%x] ", DRIVER_NAME, cmd->resp_type);
+	printf("[0x%x] ", cmd->response[0]);
+	printf("[0x%x] ", cmd->response[1]);
+	printf("[0x%x] ", cmd->response[2]);
+	printf("[0x%x] ", cmd->response[3]);
+	printf("\n");
+#endif
+
+	return 0;
+}
+
+static void mvsd_power_up(void)
+{
+#ifdef DEBUG
+	printf("%s: power up\n", DRIVER_NAME);
+#endif
+	/* disable interrupts */
+	mvsd_write(SDIO_NOR_INTR_EN, 0);
+	mvsd_write(SDIO_ERR_INTR_EN, 0);
+
+	/* SW reset */
+	mvsd_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW);
+
+	mvsd_write(SDIO_XFER_MODE, 0);
+
+	/* enable status */
+	mvsd_write(SDIO_NOR_STATUS_EN, SDIO_POLL_MASK);
+	mvsd_write(SDIO_ERR_STATUS_EN, SDIO_POLL_MASK);
+
+	/* enable interrupts status */
+	mvsd_write(SDIO_NOR_INTR_STATUS, SDIO_POLL_MASK);
+	mvsd_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK);
+}
+
+static void mvsd_power_down(void)
+{
+#ifdef DEBUG
+	printf("%s: power down\n", DRIVER_NAME);
+#endif
+	/* disable interrupts */
+	mvsd_write(SDIO_NOR_INTR_EN, 0);
+	mvsd_write(SDIO_ERR_INTR_EN, 0);
+
+	/* SW reset */
+	mvsd_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW);
+
+	mvsd_write(SDIO_XFER_MODE, SDIO_XFER_MODE_STOP_CLK);
+
+	/* disable status */
+	mvsd_write(SDIO_NOR_STATUS_EN, 0);
+	mvsd_write(SDIO_ERR_STATUS_EN, 0);
+
+	/* enable interrupts status */
+	mvsd_write(SDIO_NOR_INTR_STATUS, SDIO_POLL_MASK);
+	mvsd_write(SDIO_ERR_INTR_STATUS, SDIO_POLL_MASK);
+}
+
+static u32 mrvl_mmc_get_base_clock(void)
+{
+	u32 devid;
+	u32 revid;
+
+	if (0) {
+		kirkwood_pcie_id(&devid, &revid);
+
+#ifdef DEBUG
+		printf("%s: board devid[0x%x] devid[0x%x]\n",
+		       DRIVER_NAME, devid, revid);
+#endif
+
+		if (revid == 0 && devid != MV88F6282_DEV_ID) {
+			/* catch all Kirkwood Z0's */
+#ifdef DEBUG
+			printf("%s: Kirkwood Z0's base clock = %d\n",
+			       DRIVER_NAME, MRVL_MMC_BASE_FAST_CLK_100);
+#endif
+			return MRVL_MMC_BASE_FAST_CLK_100;
+		} else {
+#ifdef DEBUG
+			printf("%s: base clock = %d\n",
+			       DRIVER_NAME, MRVL_MMC_BASE_FAST_CLK_200);
+#endif
+			return MRVL_MMC_BASE_FAST_CLK_200;
+		}
+	}
+	return MRVL_MMC_BASE_FAST_CLOCK;
+}
+
+static void mrvl_mmc_set_clk(unsigned int clock)
+{
+	unsigned int m;
+
+	if (clock == 0) {
+#ifdef DEBUG
+		printf("%s: clock off\n", DRIVER_NAME);
+#endif
+		mvsd_write(SDIO_XFER_MODE, SDIO_XFER_MODE_STOP_CLK);
+		mvsd_write(SDIO_CLK_DIV, MRVL_MMC_BASE_DIV_MAX);
+	} else {
+		m = MRVL_MMC_BASE_FAST_CLOCK/(2*clock) - 1;
+		if (m > MRVL_MMC_BASE_DIV_MAX)
+			m = MRVL_MMC_BASE_DIV_MAX;
+#ifdef DEBUG
+		printf("%s: base = %d dividor = 0x%x clock=%d\n",
+		       DRIVER_NAME, mrvl_mmc_get_base_clock(),
+		       m, clock);
+#endif
+		mvsd_write(SDIO_CLK_DIV, m & MRVL_MMC_BASE_DIV_MAX);
+	}
+	udelay(10*1000);
+}
+
+static void mrvl_mmc_set_bus(unsigned int bus)
+{
+	u32 ctrl_reg = 0;
+
+	ctrl_reg = mvsd_read(SDIO_HOST_CTRL);
+	ctrl_reg &= ~SDIO_HOST_CTRL_DATA_WIDTH_4_BITS;
+
+	switch (bus) {
+	case 4:
+		ctrl_reg |= SDIO_HOST_CTRL_DATA_WIDTH_4_BITS;
+		break;
+	case 1:
+	default:
+		ctrl_reg |= SDIO_HOST_CTRL_DATA_WIDTH_1_BIT;
+	}
+	/* default transfer mode */
+	ctrl_reg |= SDIO_HOST_CTRL_BIG_ENDIAN;
+	ctrl_reg &= ~SDIO_HOST_CTRL_LSB_FIRST;
+
+	/* default to maximum timeout */
+	ctrl_reg |= SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX);
+
+	ctrl_reg |= SDIO_HOST_CTRL_PUSH_PULL_EN;
+
+	ctrl_reg |= SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY;
+
+	/*
+	 * The HI_SPEED_EN bit is causing trouble with many (but not all)
+	 * high speed SD, SDHC and SDIO cards.  Not enabling that bit
+	 * makes all cards work.  So let's just ignore that bit for now
+	 * and revisit this issue if problems for not enabling this bit
+	 * are ever reported.
+	 */
+#ifdef MVSDIO_TWEAK_NOSDHS
+	if (ios->timing == MMC_TIMING_MMC_HS ||
+	    ios->timing == MMC_TIMING_SD_HS)
+		ctrl_reg |= SDIO_HOST_CTRL_HI_SPEED_EN;
+#endif
+
+#ifdef DEBUG
+	printf("%s: ctrl 0x%04x: %s %s %s\n", DRIVER_NAME, ctrl_reg,
+	       (ctrl_reg & SDIO_HOST_CTRL_PUSH_PULL_EN) ?
+	       "push-pull" : "open-drain",
+	       (ctrl_reg & SDIO_HOST_CTRL_DATA_WIDTH_4_BITS) ?
+	       "4bit-width" : "1bit-width",
+	       (ctrl_reg & SDIO_HOST_CTRL_HI_SPEED_EN) ?
+	       "high-speed" : "");
+#endif
+
+	mvsd_write(SDIO_HOST_CTRL, ctrl_reg);
+	udelay(10*1000);
+}
+
+static void mrvl_mmc_set_ios(struct mmc *mmc)
+{
+#ifdef DEBUG
+	printf("%s: bus[%d] clock[%d]\n", DRIVER_NAME,
+	       mmc->bus_width, mmc->clock);
+#endif
+	mrvl_mmc_set_bus(mmc->bus_width);
+	mrvl_mmc_set_clk(mmc->clock);
+}
+
+static int mrvl_mmc_initialize(struct mmc *mmc)
+{
+#ifdef DEBUG
+	printf("%s: mrvl_mmc_initialize", DRIVER_NAME);
+#endif
+
+/*
+ * Setting host parameters
+ * Initial Host Ctrl : Timeout : max , Normal Speed mode, 4-bit data mode
+ * Big Endian, SD memory Card, Push_pull CMD Line
+ */
+	mvsd_write(SDIO_HOST_CTRL,
+		   SDIO_HOST_CTRL_TMOUT(SDIO_HOST_CTRL_TMOUT_MAX) |
+		   SDIO_HOST_CTRL_DATA_WIDTH_4_BITS |
+		   SDIO_HOST_CTRL_BIG_ENDIAN |
+		   SDIO_HOST_CTRL_PUSH_PULL_EN |
+		   SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY);
+
+	mvsd_write(SDIO_CLK_CTRL, 0);
+
+	/* enable status */
+	mvsd_write(SDIO_NOR_STATUS_EN, SDIO_POLL_MASK);
+	mvsd_write(SDIO_ERR_STATUS_EN, SDIO_POLL_MASK);
+
+	/* disable interrupts */
+	mvsd_write(SDIO_NOR_INTR_EN, 0);
+	mvsd_write(SDIO_ERR_INTR_EN, 0);
+
+	/* SW reset */
+	mvsd_write(SDIO_SW_RESET, SDIO_SW_RESET_NOW);
+
+	udelay(10*1000);
+
+	return 0;
+}
+
+static const struct mmc_ops mrvl_ops = {
+	.send_cmd	= mrvl_mmc_send_cmd,
+	.set_ios	= mrvl_mmc_set_ios,
+	.init		= mrvl_mmc_initialize,
+};
+
+static struct mmc_config mrvl_cfg = {
+	.name		= DRIVER_NAME,
+	.ops		= &mrvl_ops,
+	.f_min		= MRVL_MMC_BASE_FAST_CLOCK / MRVL_MMC_BASE_DIV_MAX,
+	.f_max		= MRVL_MMC_CLOCKRATE_MAX,
+	.voltages	= MMC_VDD_32_33 | MMC_VDD_33_34,
+	.host_caps	= MMC_MODE_4BIT | MMC_MODE_HS,
+	.part_type	= PART_TYPE_DOS,
+	.b_max		= CONFIG_SYS_MMC_MAX_BLK_COUNT,
+};
+
+int mrvl_mmc_init(bd_t *bis)
+{
+	struct mmc *mmc;
+
+#ifdef DEBUG
+	printf("%s: %s base_clock = %d", DRIVER_NAME,
+	       kirkwood_id(), mrvl_mmc_get_base_clock());
+#endif
+
+	mvsd_power_up();
+
+	mmc = mmc_create(&mrvl_cfg, bis);
+	if (mmc == NULL)
+		return -1;
+
+	return 0;
+}
+
diff --git a/include/configs/openrd.h b/include/configs/openrd.h
index 8fab6e6..c4249d2 100644
--- a/include/configs/openrd.h
+++ b/include/configs/openrd.h
@@ -49,6 +49,7 @@ 
 #define CONFIG_CMD_DHCP
 #define CONFIG_CMD_ENV
 #define CONFIG_CMD_MII
+#define CONFIG_CMD_MMC
 #define CONFIG_CMD_NAND
 #define CONFIG_CMD_PING
 #define CONFIG_CMD_USB
@@ -123,4 +124,11 @@ 
 #define CONFIG_SYS_ATA_IDE1_OFFSET	MV_SATA_PORT1_OFFSET
 #endif /*CONFIG_MVSATA_IDE*/
 
+#ifdef CONFIG_CMD_MMC
+#define CONFIG_MMC
+#define CONFIG_GENERIC_MMC
+#define CONFIG_MRVL_MMC
+#define CONFIG_SYS_MMC_BASE KW_SDIO_BASE
+#endif /* CONFIG_CMD_MMC */
+
 #endif /* _CONFIG_OPENRD_BASE_H */
diff --git a/include/configs/sheevaplug.h b/include/configs/sheevaplug.h
index ecc93bc..afba340 100644
--- a/include/configs/sheevaplug.h
+++ b/include/configs/sheevaplug.h
@@ -23,6 +23,8 @@ 
 #define CONFIG_MACH_SHEEVAPLUG	/* Machine type */
 #define CONFIG_SKIP_LOWLEVEL_INIT	/* disable board lowlevel_init */
 
+#define CONFIG_ARCH_DEV_ID	1
+
 /*
  * Commands configuration
  */
@@ -31,9 +33,11 @@ 
 #define CONFIG_CMD_DHCP
 #define CONFIG_CMD_ENV
 #define CONFIG_CMD_MII
+#define CONFIG_CMD_MMC
 #define CONFIG_CMD_NAND
 #define CONFIG_CMD_PING
 #define CONFIG_CMD_USB
+
 /*
  * mv-common.h should be defined after CMD configs since it used them
  * to enable certain macros
@@ -49,13 +53,14 @@ 
 #else
 #define CONFIG_ENV_IS_NOWHERE		1	/* if env in SDRAM */
 #endif
+
 /*
  * max 4k env size is enough, but in case of nand
  * it has to be rounded to sector size
  */
 #define CONFIG_ENV_SIZE			0x20000	/* 128k */
-#define CONFIG_ENV_ADDR			0x60000
-#define CONFIG_ENV_OFFSET		0x60000	/* env starts here */
+#define CONFIG_ENV_ADDR			0x80000
+#define CONFIG_ENV_OFFSET		0x80000	/* env starts here */
 
 /*
  * Default environment variables
@@ -82,6 +87,16 @@ 
 #endif /* CONFIG_CMD_NET */
 
 /*
+ * SDIO/MMC Card Configuration
+ */
+#ifdef CONFIG_CMD_MMC
+#define CONFIG_MMC
+#define CONFIG_GENERIC_MMC
+#define CONFIG_MRVL_MMC
+#define CONFIG_SYS_MMC_BASE KW_SDIO_BASE
+#endif /* CONFIG_CMD_MMC */
+
+/*
  * File system
  */
 #define CONFIG_CMD_EXT2
diff --git a/include/mrvl_mmc.h b/include/mrvl_mmc.h
new file mode 100644
index 0000000..242a86c
--- /dev/null
+++ b/include/mrvl_mmc.h
@@ -0,0 +1,290 @@ 
+/*
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * (C) Copyright 2012
+ * Marvell Semiconductor <www.marvell.com>
+ * Written-by: Gérald Kerma <dreagle@doukki.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.
+ */
+
+#ifndef __MRVL_MMC_H__
+#define __MRVL_MMC_H__
+
+/* needed for the mmc_cfg definition */
+#include <mmc.h>
+
+#define MMC_BLOCK_SIZE				512
+
+/*
+ * Clock rates
+ */
+
+#define MRVL_MMC_CLOCKRATE_MAX			50000000
+#define MRVL_MMC_BASE_DIV_MAX			0x7ff
+#define MRVL_MMC_BASE_FAST_CLOCK		CONFIG_SYS_TCLK
+#define MRVL_MMC_BASE_FAST_CLK_100		100000000
+#define MRVL_MMC_BASE_FAST_CLK_200		200000000
+
+/* SDIO register */
+#define SDIO_SYS_ADDR_LOW			0x000
+#define SDIO_SYS_ADDR_HI			0x004
+#define SDIO_BLK_SIZE				0x008
+#define SDIO_BLK_COUNT				0x00c
+#define SDIO_ARG_LOW				0x010
+#define SDIO_ARG_HI				0x014
+#define SDIO_XFER_MODE				0x018
+#define SDIO_CMD				0x01c
+#define SDIO_RSP(i)				(0x020 + ((i)<<2))
+#define SDIO_RSP0				0x020
+#define SDIO_RSP1				0x024
+#define SDIO_RSP2				0x028
+#define SDIO_RSP3				0x02c
+#define SDIO_RSP4				0x030
+#define SDIO_RSP5				0x034
+#define SDIO_RSP6				0x038
+#define SDIO_RSP7				0x03c
+#define SDIO_BUF_DATA_PORT			0x040
+#define SDIO_RSVED				0x044
+#define SDIO_HW_STATE				0x048
+#define SDIO_PRESENT_STATE0			0x048
+#define SDIO_PRESENT_STATE1			0x04c
+#define SDIO_HOST_CTRL				0x050
+#define SDIO_BLK_GAP_CTRL			0x054
+#define SDIO_CLK_CTRL				0x058
+#define SDIO_SW_RESET				0x05c
+#define SDIO_NOR_INTR_STATUS			0x060
+#define SDIO_ERR_INTR_STATUS			0x064
+#define SDIO_NOR_STATUS_EN			0x068
+#define SDIO_ERR_STATUS_EN			0x06c
+#define SDIO_NOR_INTR_EN			0x070
+#define SDIO_ERR_INTR_EN			0x074
+#define SDIO_AUTOCMD12_ERR_STATUS		0x078
+#define SDIO_CURR_BYTE_LEFT			0x07c
+#define SDIO_CURR_BLK_LEFT			0x080
+#define SDIO_AUTOCMD12_ARG_LOW			0x084
+#define SDIO_AUTOCMD12_ARG_HI			0x088
+#define SDIO_AUTOCMD12_INDEX			0x08c
+#define SDIO_AUTO_RSP(i)			(0x090 + ((i)<<2))
+#define SDIO_AUTO_RSP0				0x090
+#define SDIO_AUTO_RSP1				0x094
+#define SDIO_AUTO_RSP2				0x098
+#define SDIO_CLK_DIV				0x128
+
+#define WINDOW_CTRL(i)				(0x108 + ((i) << 3))
+#define WINDOW_BASE(i)				(0x10c + ((i) << 3))
+
+/* SDIO_PRESENT_STATE */
+#define CARD_BUSY				(1 << 1)
+#define CMD_INHIBIT				(1 << 0)
+#define CMD_TXACTIVE				(1 << 8)
+#define CMD_RXACTIVE				(1 << 9)
+#define CMD_AUTOCMD12ACTIVE			(1 << 14)
+#define CMD_BUS_BUSY				(CMD_AUTOCMD12ACTIVE |	\
+						CMD_RXACTIVE |	\
+						CMD_TXACTIVE |	\
+						CMD_INHIBIT |	\
+						CARD_BUSY)
+
+/*
+ * SDIO_CMD
+ */
+
+#define SDIO_CMD_RSP_NONE			(0 << 0)
+#define SDIO_CMD_RSP_136			(1 << 0)
+#define SDIO_CMD_RSP_48				(2 << 0)
+#define SDIO_CMD_RSP_48BUSY			(3 << 0)
+
+#define SDIO_CMD_CHECK_DATACRC16		(1 << 2)
+#define SDIO_CMD_CHECK_CMDCRC			(1 << 3)
+#define SDIO_CMD_INDX_CHECK			(1 << 4)
+#define SDIO_CMD_DATA_PRESENT			(1 << 5)
+#define SDIO_UNEXPECTED_RESP			(1 << 7)
+
+#define SDIO_CMD_INDEX(x)			((x) << 8)
+
+/*
+ * SDIO_XFER_MODE
+ */
+
+#define SDIO_XFER_MODE_STOP_CLK			(1 << 5)
+#define SDIO_XFER_MODE_HW_WR_DATA_EN		(1 << 1)
+#define SDIO_XFER_MODE_AUTO_CMD12		(1 << 2)
+#define SDIO_XFER_MODE_INT_CHK_EN		(1 << 3)
+#define SDIO_XFER_MODE_TO_HOST			(1 << 4)
+#define SDIO_XFER_MODE_DMA			(0 << 6)
+
+/*
+ * SDIO_HOST_CTRL
+ */
+
+#define SDIO_HOST_CTRL_PUSH_PULL_EN		(1 << 0)
+
+#define SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY	(0 << 1)
+#define SDIO_HOST_CTRL_CARD_TYPE_IO_ONLY	(1 << 1)
+#define SDIO_HOST_CTRL_CARD_TYPE_IO_MEM_COMBO	(2 << 1)
+#define SDIO_HOST_CTRL_CARD_TYPE_IO_MMC		(3 << 1)
+#define SDIO_HOST_CTRL_CARD_TYPE_MASK		(3 << 1)
+
+#define SDIO_HOST_CTRL_BIG_ENDIAN		(1 << 3)
+#define SDIO_HOST_CTRL_LSB_FIRST		(1 << 4)
+#define SDIO_HOST_CTRL_DATA_WIDTH_1_BIT		(0 << 9)
+#define SDIO_HOST_CTRL_DATA_WIDTH_4_BITS	(1 << 9)
+#define SDIO_HOST_CTRL_HI_SPEED_EN		(1 << 10)
+
+#define SDIO_HOST_CTRL_TMOUT_MAX		0xf
+#define SDIO_HOST_CTRL_TMOUT_MASK		(0xf << 11)
+#define SDIO_HOST_CTRL_TMOUT(x)			((x) << 11)
+#define SDIO_HOST_CTRL_TMOUT_EN			(1 << 15)
+
+/*
+ * SDIO_SW_RESET
+ */
+
+#define SDIO_SW_RESET_NOW			(1 << 8)
+
+/*
+ * Normal interrupt status bits
+ */
+
+#define SDIO_NOR_ERROR				(1 << 15)
+#define SDIO_NOR_UNEXP_RSP			(1 << 14)
+#define SDIO_NOR_AUTOCMD12_DONE			(1 << 13)
+#define SDIO_NOR_SUSPEND_ON			(1 << 12)
+#define SDIO_NOR_LMB_FF_8W_AVAIL		(1 << 11)
+#define SDIO_NOR_LMB_FF_8W_FILLED		(1 << 10)
+#define SDIO_NOR_READ_WAIT_ON			(1 << 9)
+#define SDIO_NOR_CARD_INT			(1 << 8)
+#define SDIO_NOR_READ_READY			(1 << 5)
+#define SDIO_NOR_WRITE_READY			(1 << 4)
+#define SDIO_NOR_DMA_INI			(1 << 3)
+#define SDIO_NOR_BLK_GAP_EVT			(1 << 2)
+#define SDIO_NOR_XFER_DONE			(1 << 1)
+#define SDIO_NOR_CMD_DONE			(1 << 0)
+
+/*
+ * Error status bits
+ */
+
+#define SDIO_ERR_CRC_STATUS			(1 << 14)
+#define SDIO_ERR_CRC_STARTBIT			(1 << 13)
+#define SDIO_ERR_CRC_ENDBIT			(1 << 12)
+#define SDIO_ERR_RESP_TBIT			(1 << 11)
+#define SDIO_ERR_XFER_SIZE			(1 << 10)
+#define SDIO_ERR_CMD_STARTBIT			(1 << 9)
+#define SDIO_ERR_AUTOCMD12			(1 << 8)
+#define SDIO_ERR_DATA_ENDBIT			(1 << 6)
+#define SDIO_ERR_DATA_CRC			(1 << 5)
+#define SDIO_ERR_DATA_TIMEOUT			(1 << 4)
+#define SDIO_ERR_CMD_INDEX			(1 << 3)
+#define SDIO_ERR_CMD_ENDBIT			(1 << 2)
+#define SDIO_ERR_CMD_CRC			(1 << 1)
+#define SDIO_ERR_CMD_TIMEOUT			(1 << 0)
+/* enable all for polling */
+#define SDIO_POLL_MASK				0xffff
+
+/*
+ * CMD12 error status bits
+ */
+
+#define SDIO_AUTOCMD12_ERR_NOTEXE		(1 << 0)
+#define SDIO_AUTOCMD12_ERR_TIMEOUT		(1 << 1)
+#define SDIO_AUTOCMD12_ERR_CRC			(1 << 2)
+#define SDIO_AUTOCMD12_ERR_ENDBIT		(1 << 3)
+#define SDIO_AUTOCMD12_ERR_INDEX		(1 << 4)
+#define SDIO_AUTOCMD12_ERR_RESP_T_BIT		(1 << 5)
+#define SDIO_AUTOCMD12_ERR_RESP_STARTBIT	(1 << 6)
+
+#define MMC_RSP_PRESENT				(1 << 0)
+/* 136 bit response */
+#define MMC_RSP_136				(1 << 1)
+/* expect valid crc */
+#define MMC_RSP_CRC				(1 << 2)
+/* card may send busy */
+#define MMC_RSP_BUSY				(1 << 3)
+/* response contains opcode */
+#define MMC_RSP_OPCODE				(1 << 4)
+
+#define MMC_BUSMODE_OPENDRAIN			1
+#define MMC_BUSMODE_PUSHPULL			2
+
+#define MMC_BUS_WIDTH_1				0
+#define MMC_BUS_WIDTH_4				2
+#define MMC_BUS_WIDTH_8				3
+
+/* Can the host do 4 bit transfers */
+#define MMC_CAP_4_BIT_DATA			(1 << 0)
+/* Can do MMC high-speed timing */
+#define MMC_CAP_MMC_HIGHSPEED			(1 << 1)
+/* Can do SD high-speed timing */
+#define MMC_CAP_SD_HIGHSPEED			(1 << 2)
+/* Can signal pending SDIO IRQs */
+#define MMC_CAP_SDIO_IRQ			(1 << 3)
+/* Talks only SPI protocols */
+#define MMC_CAP_SPI				(1 << 4)
+/* Needs polling for card-detection */
+#define MMC_CAP_NEEDS_POLL			(1 << 5)
+/* Can the host do 8 bit transfers */
+#define MMC_CAP_8_BIT_DATA			(1 << 6)
+
+/* Nonremovable e.g. eMMC */
+#define MMC_CAP_NONREMOVABLE			(1 << 8)
+/* Waits while card is busy */
+#define MMC_CAP_WAIT_WHILE_BUSY			(1 << 9)
+/* Allow erase/trim commands */
+#define MMC_CAP_ERASE				(1 << 10)
+/* can support DDR mode at 1.8V */
+#define MMC_CAP_1_8V_DDR			(1 << 11)
+/* can support DDR mode at 1.2V */
+#define MMC_CAP_1_2V_DDR			(1 << 12)
+/* Can power off after boot */
+#define MMC_CAP_POWER_OFF_CARD			(1 << 13)
+/* CMD14/CMD19 bus width ok */
+#define MMC_CAP_BUS_WIDTH_TEST			(1 << 14)
+/* Host supports UHS SDR12 mode */
+#define MMC_CAP_UHS_SDR12			(1 << 15)
+/* Host supports UHS SDR25 mode */
+#define MMC_CAP_UHS_SDR25			(1 << 16)
+/* Host supports UHS SDR50 mode */
+#define MMC_CAP_UHS_SDR50			(1 << 17)
+/* Host supports UHS SDR104 mode */
+#define MMC_CAP_UHS_SDR104			(1 << 18)
+/* Host supports UHS DDR50 mode */
+#define MMC_CAP_UHS_DDR50			(1 << 19)
+/* Host supports Driver Type A */
+#define MMC_CAP_DRIVER_TYPE_A			(1 << 23)
+/* Host supports Driver Type C */
+#define MMC_CAP_DRIVER_TYPE_C			(1 << 24)
+/* Host supports Driver Type D */
+#define MMC_CAP_DRIVER_TYPE_D			(1 << 25)
+/* CMD23 supported. */
+#define MMC_CAP_CMD23				(1 << 30)
+/* Hardware reset */
+#define MMC_CAP_HW_RESET			(1 << 31)
+
+
+struct mrvl_cfg {
+	u32	mvsdio_base;
+	u32	mvsdio_clk;
+	u8	max_bus_width;
+	struct mmc_config cfg;
+};
+
+/*
+ * Functions prototypes
+ */
+
+int mrvl_mmc_init(bd_t *bis);
+
+#endif /* __MRVL_MMC_H__ */