]> git.ipfire.org Git - people/ms/u-boot.git/commitdiff
Merge branch 'master' of git://git.denx.de/u-boot-mmc
authorWolfgang Denk <wd@denx.de>
Tue, 19 Jul 2011 20:27:07 +0000 (22:27 +0200)
committerWolfgang Denk <wd@denx.de>
Tue, 19 Jul 2011 20:27:07 +0000 (22:27 +0200)
* 'master' of git://git.denx.de/u-boot-mmc:
  mmc: rescan fails on empty slot
  AT91:mmc:fix multiple read/write error
  mmc: Access mode validation for eMMC cards > 2 GiB
  mmc: sh_mmcif: add support for Renesas MMCIF
  mmc: fix the condition for MMC version 4
  MMC: add marvell sdhci driver
  MMC: add sdhci generic framework
  MMC: add erase function to both mmc and sd
  MMC: unify mmc read and write operation
  mmc: Tegra2: Enable SD/MMC driver for Seaboard and Harmony
  mmc: Tegra2: SD/MMC driver for Seaboard - eMMC on SDMMC4, SDIO on SDMMC3

22 files changed:
README
arch/arm/include/asm/arch-tegra2/clk_rst.h
arch/arm/include/asm/arch-tegra2/pinmux.h
board/nvidia/common/board.c
board/nvidia/common/board.h
common/cmd_mmc.c
drivers/mmc/Makefile
drivers/mmc/atmel_mci.h
drivers/mmc/gen_atmel_mci.c
drivers/mmc/mmc.c
drivers/mmc/mv_sdhci.c [new file with mode: 0644]
drivers/mmc/s5p_mmc.c
drivers/mmc/sdhci.c [new file with mode: 0644]
drivers/mmc/sh_mmcif.c [new file with mode: 0644]
drivers/mmc/sh_mmcif.h [new file with mode: 0644]
drivers/mmc/tegra2_mmc.c [new file with mode: 0644]
drivers/mmc/tegra2_mmc.h [new file with mode: 0644]
include/configs/harmony.h
include/configs/seaboard.h
include/mmc.h
include/part.h
include/sdhci.h [new file with mode: 0644]

diff --git a/README b/README
index 3239985416dd72f38ff5d9c06e733327fb7df9eb..294b39eda248a8bc3b74cec798ab9a5dcd6253c8 100644 (file)
--- a/README
+++ b/README
@@ -1064,6 +1064,15 @@ The following options need to be configured:
                enabled with CONFIG_CMD_MMC. The MMC driver also works with
                the FAT fs. This is enabled with CONFIG_CMD_FAT.
 
+               CONFIG_SH_MMCIF
+               Support for Renesas on-chip MMCIF controller
+
+                       CONFIG_SH_MMCIF_ADDR
+                       Define the base address of MMCIF registers
+
+                       CONFIG_SH_MMCIF_CLK
+                       Define the clock frequency for MMCIF
+
 - Journaling Flash filesystem support:
                CONFIG_JFFS2_NAND, CONFIG_JFFS2_NAND_OFF, CONFIG_JFFS2_NAND_SIZE,
                CONFIG_JFFS2_NAND_DEV
index bd8ad2ca0499118db3de5ee8e094af2e35d4b3a7..36e27b5f3fa72cc087b2de9cf826dbe71651e33d 100644 (file)
@@ -191,4 +191,9 @@ struct clk_rst_ctlr {
 
 #define CPCON                  (1 << 8)
 
+#define SWR_SDMMC4_RST         (1 << 15)
+#define CLK_ENB_SDMMC4         (1 << 15)
+#define SWR_SDMMC3_RST         (1 << 5)
+#define CLK_ENB_SDMMC3         (1 << 5)
+
 #endif /* CLK_RST_H */
index 8b4bd8d883e0b96c8ae804a7a72447cb0f189f8a..cce936ddc80e059fdabfe54194d01296e397ae1b 100644 (file)
@@ -51,5 +51,11 @@ struct pmux_tri_ctlr {
 #define Z_GMC                  (1 << 29)
 #define Z_IRRX                 (1 << 20)
 #define Z_IRTX                 (1 << 19)
+#define Z_GMA                  (1 << 28)
+#define Z_GME                  (1 << 0)
+#define Z_ATB                  (1 << 1)
+#define Z_SDB                  (1 << 15)
+#define Z_SDC                  (1 << 1)
+#define Z_SDD                  (1 << 2)
 
 #endif /* PINMUX_H */
index 3d6c248479327e896fcc92722e51c00ec08b43ea..627e061798f60ccdbcf2e2cf10cafc3100651905 100644 (file)
 #include <asm/arch/uart.h>
 #include "board.h"
 
+#ifdef CONFIG_TEGRA2_MMC
+#include <mmc.h>
+#endif
+
 DECLARE_GLOBAL_DATA_PTR;
 
 const struct tegra2_sysinfo sysinfo = {
@@ -170,6 +174,116 @@ static void pin_mux_uart(void)
 #endif /* CONFIG_TEGRA2_ENABLE_UARTD */
 }
 
+/*
+ * Routine: clock_init_mmc
+ * Description: init the PLL and clocks for the SDMMC controllers
+ */
+static void clock_init_mmc(void)
+{
+       struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+       u32 reg;
+
+       /* Do the SDMMC resets/clock enables */
+
+       /* Assert Reset to SDMMC4 */
+       reg = readl(&clkrst->crc_rst_dev_l);
+       reg |= SWR_SDMMC4_RST;          /* SWR_SDMMC4_RST = 1 */
+       writel(reg, &clkrst->crc_rst_dev_l);
+
+       /* Enable clk to SDMMC4 */
+       reg = readl(&clkrst->crc_clk_out_enb_l);
+       reg |= CLK_ENB_SDMMC4;          /* CLK_ENB_SDMMC4 = 1 */
+       writel(reg, &clkrst->crc_clk_out_enb_l);
+
+       /* Enable pllp_out0 to SDMMC4 */
+       reg = readl(&clkrst->crc_clk_src_sdmmc4);
+       reg &= 0x3FFFFF00;      /* SDMMC4_CLK_SRC = 00, PLLP_OUT0 */
+       reg |= (10 << 1);       /* n-1, 11-1 shl 1 */
+       writel(reg, &clkrst->crc_clk_src_sdmmc4);
+
+       /*
+        * As per the Tegra2 TRM, section 5.3.4:
+        * 'Wait 2 us for the clock to flush through the pipe/logic'
+        */
+       udelay(2);
+
+       /* De-assert reset to SDMMC4 */
+       reg = readl(&clkrst->crc_rst_dev_l);
+       reg &= ~SWR_SDMMC4_RST;         /* SWR_SDMMC4_RST = 0 */
+       writel(reg, &clkrst->crc_rst_dev_l);
+
+       /* Assert Reset to SDMMC3 */
+       reg = readl(&clkrst->crc_rst_dev_u);
+       reg |= SWR_SDMMC3_RST;          /* SWR_SDMMC3_RST = 1 */
+       writel(reg, &clkrst->crc_rst_dev_u);
+
+       /* Enable clk to SDMMC3 */
+       reg = readl(&clkrst->crc_clk_out_enb_u);
+       reg |= CLK_ENB_SDMMC3;          /* CLK_ENB_SDMMC3 = 1 */
+       writel(reg, &clkrst->crc_clk_out_enb_u);
+
+       /* Enable pllp_out0 to SDMMC4, set divisor to 11 for 20MHz */
+       reg = readl(&clkrst->crc_clk_src_sdmmc3);
+       reg &= 0x3FFFFF00;      /* SDMMC3_CLK_SRC = 00, PLLP_OUT0 */
+       reg |= (10 << 1);       /* n-1, 11-1 shl 1 */
+       writel(reg, &clkrst->crc_clk_src_sdmmc3);
+
+       /* wait for 2us */
+       udelay(2);
+
+       /* De-assert reset to SDMMC3 */
+       reg = readl(&clkrst->crc_rst_dev_u);
+       reg &= ~SWR_SDMMC3_RST;         /* SWR_SDMMC3_RST = 0 */
+       writel(reg, &clkrst->crc_rst_dev_u);
+}
+
+/*
+ * Routine: pin_mux_mmc
+ * Description: setup the pin muxes/tristate values for the SDMMC(s)
+ */
+static void pin_mux_mmc(void)
+{
+       struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
+       u32 reg;
+
+       /* SDMMC4 */
+       /* config 2, x8 on 2nd set of pins */
+       reg = readl(&pmt->pmt_ctl_a);
+       reg |= (3 << 16);       /* ATB_SEL [17:16] = 11 SDIO4 */
+       writel(reg, &pmt->pmt_ctl_a);
+       reg = readl(&pmt->pmt_ctl_b);
+       reg |= (3 << 0);        /* GMA_SEL [1:0] = 11 SDIO4 */
+       writel(reg, &pmt->pmt_ctl_b);
+       reg = readl(&pmt->pmt_ctl_d);
+       reg |= (3 << 0);        /* GME_SEL [1:0] = 11 SDIO4 */
+       writel(reg, &pmt->pmt_ctl_d);
+
+       reg = readl(&pmt->pmt_tri_a);
+       reg &= ~Z_ATB;          /* Z_ATB = normal (0) */
+       reg &= ~Z_GMA;          /* Z_GMA = normal (0) */
+       writel(reg, &pmt->pmt_tri_a);
+       reg = readl(&pmt->pmt_tri_b);
+       reg &= ~Z_GME;          /* Z_GME = normal (0) */
+       writel(reg, &pmt->pmt_tri_b);
+
+       /* SDMMC3 */
+       /* SDIO3_CLK, SDIO3_CMD, SDIO3_DAT[3:0] */
+       reg = readl(&pmt->pmt_ctl_d);
+       reg &= 0xFFFF03FF;
+       reg |= (2 << 10);       /* SDB_SEL [11:10] = 01 SDIO3 */
+       reg |= (2 << 12);       /* SDC_SEL [13:12] = 01 SDIO3 */
+       reg |= (2 << 14);       /* SDD_SEL [15:14] = 01 SDIO3 */
+       writel(reg, &pmt->pmt_ctl_d);
+
+       reg = readl(&pmt->pmt_tri_b);
+       reg &= ~Z_SDC;          /* Z_SDC = normal (0) */
+       reg &= ~Z_SDD;          /* Z_SDD = normal (0) */
+       writel(reg, &pmt->pmt_tri_b);
+       reg = readl(&pmt->pmt_tri_d);
+       reg &= ~Z_SDB;          /* Z_SDB = normal (0) */
+       writel(reg, &pmt->pmt_tri_d);
+}
+
 /*
  * Routine: clock_init
  * Description: Do individual peripheral clock reset/enables
@@ -210,3 +324,36 @@ int board_init(void)
 
        return 0;
 }
+
+#ifdef CONFIG_TEGRA2_MMC
+/* this is a weak define that we are overriding */
+int board_mmc_init(bd_t *bd)
+{
+       debug("board_mmc_init called\n");
+       /* Enable clocks, muxes, etc. for SDMMC controllers */
+       clock_init_mmc();
+       pin_mux_mmc();
+
+       debug("board_mmc_init: init eMMC\n");
+       /* init dev 0, eMMC chip, with 4-bit bus */
+       tegra2_mmc_init(0, 4);
+
+       debug("board_mmc_init: init SD slot\n");
+       /* init dev 1, SD slot, with 4-bit bus */
+       tegra2_mmc_init(1, 4);
+
+       return 0;
+}
+
+/* this is a weak define that we are overriding */
+int board_mmc_getcd(u8 *cd, struct mmc *mmc)
+{
+       debug("board_mmc_getcd called\n");
+       /*
+        * Hard-code CD presence for now. Need to add GPIO inputs
+        * for Seaboard & Harmony (& Kaen/Aebl/Wario?)
+        */
+       *cd = 1;
+       return 0;
+}
+#endif
index 350bc5750e71b68ee143e4814ffb5eea218473e2..4334c028d38828cfb12c9efed949046762e4ba2b 100644 (file)
@@ -29,5 +29,6 @@ void clock_init(void);
 void pinmux_init(void);
 void gpio_init(void);
 void gpio_config_uart(void);
+int tegra2_mmc_init(int dev_index, int bus_width);
 
 #endif /* BOARD_H */
index 176646d4627bfa18e5b627d28956629ce78246ef..8f13c22d9baba16c2ab6a975a129901987618b33 100644 (file)
@@ -87,6 +87,12 @@ U_BOOT_CMD(
 );
 #else /* !CONFIG_GENERIC_MMC */
 
+enum mmc_state {
+       MMC_INVALID,
+       MMC_READ,
+       MMC_WRITE,
+       MMC_ERASE,
+};
 static void print_mmcinfo(struct mmc *mmc)
 {
        printf("Device: %s\n", mmc->name);
@@ -144,6 +150,8 @@ U_BOOT_CMD(
 
 int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
+       enum mmc_state state;
+
        if (argc < 2)
                return cmd_usage(cmdtp);
 
@@ -165,9 +173,11 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
                }
 
                mmc->has_init = 0;
-               mmc_init(mmc);
 
-               return 0;
+               if (mmc_init(mmc))
+                       return 1;
+               else
+                       return 0;
        } else if (strncmp(argv[1], "part", 4) == 0) {
                block_dev_desc_t *mmc_dev;
                struct mmc *mmc = find_mmc_device(curr_device);
@@ -239,53 +249,61 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
                                curr_device, mmc->part_num);
 
                return 0;
-       } else if (strcmp(argv[1], "read") == 0) {
-               void *addr = (void *)simple_strtoul(argv[2], NULL, 16);
-               u32 cnt = simple_strtoul(argv[4], NULL, 16);
-               u32 n;
-               u32 blk = simple_strtoul(argv[3], NULL, 16);
-               struct mmc *mmc = find_mmc_device(curr_device);
-
-               if (!mmc) {
-                       printf("no mmc device at slot %x\n", curr_device);
-                       return 1;
-               }
-
-               printf("\nMMC read: dev # %d, block # %d, count %d ... ",
-                               curr_device, blk, cnt);
-
-               mmc_init(mmc);
-
-               n = mmc->block_dev.block_read(curr_device, blk, cnt, addr);
+       }
 
-               /* flush cache after read */
-               flush_cache((ulong)addr, cnt * 512); /* FIXME */
+       if (strcmp(argv[1], "read") == 0)
+               state = MMC_READ;
+       else if (strcmp(argv[1], "write") == 0)
+               state = MMC_WRITE;
+       else if (strcmp(argv[1], "erase") == 0)
+               state = MMC_ERASE;
+       else
+               state = MMC_INVALID;
 
-               printf("%d blocks read: %s\n",
-                               n, (n==cnt) ? "OK" : "ERROR");
-               return (n == cnt) ? 0 : 1;
-       } else if (strcmp(argv[1], "write") == 0) {
-               void *addr = (void *)simple_strtoul(argv[2], NULL, 16);
-               u32 cnt = simple_strtoul(argv[4], NULL, 16);
-               u32 n;
+       if (state != MMC_INVALID) {
                struct mmc *mmc = find_mmc_device(curr_device);
+               int idx = 2;
+               u32 blk, cnt, n;
+               void *addr;
 
-               int blk = simple_strtoul(argv[3], NULL, 16);
+               if (state != MMC_ERASE) {
+                       addr = (void *)simple_strtoul(argv[idx], NULL, 16);
+                       ++idx;
+               } else
+                       addr = 0;
+               blk = simple_strtoul(argv[idx], NULL, 16);
+               cnt = simple_strtoul(argv[idx + 1], NULL, 16);
 
                if (!mmc) {
                        printf("no mmc device at slot %x\n", curr_device);
                        return 1;
                }
 
-               printf("\nMMC write: dev # %d, block # %d, count %d ... ",
-                               curr_device, blk, cnt);
+               printf("\nMMC %s: dev # %d, block # %d, count %d ... ",
+                               argv[1], curr_device, blk, cnt);
 
                mmc_init(mmc);
 
-               n = mmc->block_dev.block_write(curr_device, blk, cnt, addr);
+               switch (state) {
+               case MMC_READ:
+                       n = mmc->block_dev.block_read(curr_device, blk,
+                                                     cnt, addr);
+                       /* flush cache after read */
+                       flush_cache((ulong)addr, cnt * 512); /* FIXME */
+                       break;
+               case MMC_WRITE:
+                       n = mmc->block_dev.block_write(curr_device, blk,
+                                                     cnt, addr);
+                       break;
+               case MMC_ERASE:
+                       n = mmc->block_dev.block_erase(curr_device, blk, cnt);
+                       break;
+               default:
+                       BUG();
+               }
 
-               printf("%d blocks written: %s\n",
-                               n, (n == cnt) ? "OK" : "ERROR");
+               printf("%d blocks %s: %s\n",
+                               n, argv[1], (n == cnt) ? "OK" : "ERROR");
                return (n == cnt) ? 0 : 1;
        }
 
@@ -297,6 +315,7 @@ U_BOOT_CMD(
        "MMC sub system",
        "read addr blk# cnt\n"
        "mmc write addr blk# cnt\n"
+       "mmc erase blk# cnt\n"
        "mmc rescan\n"
        "mmc part - lists available partition on current mmc device\n"
        "mmc dev [dev] [part] - show or set current mmc device [partition]\n"
index a8fe17a6f7bffd14769e8d09a0f5565c181282d9..3968c14bb09cd73ff73b2096d48587cda170c544 100644 (file)
@@ -33,11 +33,15 @@ COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o
 COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o
 COBJS-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o
+COBJS-$(CONFIG_MV_SDHCI) += mv_sdhci.o
 COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
 COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o
 COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o
 COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o
+COBJS-$(CONFIG_SDHCI) += sdhci.o
+COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o
+COBJS-$(CONFIG_TEGRA2_MMC) += tegra2_mmc.o
 
 COBJS  := $(COBJS-y)
 SRCS   := $(COBJS:.o=.c)
index 90ab6a8a6d088436ffd9323d59f62b0c35436529..3095d22e68d8dc34c02dfbf5a504e1ec95740a59 100644 (file)
@@ -36,7 +36,7 @@ typedef struct atmel_mci {
        u32     sdcr;   /* 0x0c */
        u32     argr;   /* 0x10 */
        u32     cmdr;   /* 0x14 */
-       u32     _18;    /* 0x18 */
+       u32     blkr;   /* 0x18 */
        u32     _1c;    /* 0x1c */
        u32     rspr;   /* 0x20 */
        u32     rspr1;  /* 0x24 */
@@ -67,6 +67,7 @@ typedef struct atmel_mci {
 #define MMCI_SDCR                              0x000c
 #define MMCI_ARGR                              0x0010
 #define MMCI_CMDR                              0x0014
+#define MMCI_BLKR                              0x0018
 #define MMCI_RSPR                              0x0020
 #define MMCI_RSPR1                             0x0024
 #define MMCI_RSPR2                             0x0028
@@ -140,6 +141,12 @@ typedef struct atmel_mci {
 #define MMCI_TRTYP_OFFSET                      19
 #define MMCI_TRTYP_SIZE                                2
 
+/* Bitfields in BLKR */
+#define MMCI_BCNT_OFFSET                       0
+#define MMCI_BCNT_SIZE                         16
+#define MMCI_BLKLEN_OFFSET                     16
+#define MMCI_BLKLEN_SIZE                       16
+
 /* Bitfields in RSPRx */
 #define MMCI_RSP_OFFSET                                0
 #define MMCI_RSP_SIZE                          32
index f346b244be124e0502a9ec172584f4fccee78c2b..d217574b57f7a3b0f34259a5c7616753d838ce28 100644 (file)
@@ -183,6 +183,10 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
        /* Figure out the transfer arguments */
        cmdr = mci_encode_cmd(cmd, data, &error_flags);
 
+       if (data)
+               writel(MMCI_BF(BCNT, data->blocks) |
+                       MMCI_BF(BLKLEN, mmc->read_bl_len), &mci->blkr);
+
        /* Send the command */
        writel(cmd->cmdarg, &mci->argr);
        writel(cmdr, &mci->cmdr);
index 21aedbaa3fe2e27e9f1e82a6c4ed3b0f66ad4550..cbd75673b8b501b23bcea85c79253a7f737868f3 100644 (file)
@@ -174,6 +174,88 @@ struct mmc *find_mmc_device(int dev_num)
        return NULL;
 }
 
+static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt)
+{
+       struct mmc_cmd cmd;
+       ulong end;
+       int err, start_cmd, end_cmd;
+
+       if (mmc->high_capacity)
+               end = start + blkcnt - 1;
+       else {
+               end = (start + blkcnt - 1) * mmc->write_bl_len;
+               start *= mmc->write_bl_len;
+       }
+
+       if (IS_SD(mmc)) {
+               start_cmd = SD_CMD_ERASE_WR_BLK_START;
+               end_cmd = SD_CMD_ERASE_WR_BLK_END;
+       } else {
+               start_cmd = MMC_CMD_ERASE_GROUP_START;
+               end_cmd = MMC_CMD_ERASE_GROUP_END;
+       }
+
+       cmd.cmdidx = start_cmd;
+       cmd.cmdarg = start;
+       cmd.resp_type = MMC_RSP_R1;
+       cmd.flags = 0;
+
+       err = mmc_send_cmd(mmc, &cmd, NULL);
+       if (err)
+               goto err_out;
+
+       cmd.cmdidx = end_cmd;
+       cmd.cmdarg = end;
+
+       err = mmc_send_cmd(mmc, &cmd, NULL);
+       if (err)
+               goto err_out;
+
+       cmd.cmdidx = MMC_CMD_ERASE;
+       cmd.cmdarg = SECURE_ERASE;
+       cmd.resp_type = MMC_RSP_R1b;
+
+       err = mmc_send_cmd(mmc, &cmd, NULL);
+       if (err)
+               goto err_out;
+
+       return 0;
+
+err_out:
+       puts("mmc erase failed\n");
+       return err;
+}
+
+static unsigned long
+mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt)
+{
+       int err = 0;
+       struct mmc *mmc = find_mmc_device(dev_num);
+       lbaint_t blk = 0, blk_r = 0;
+
+       if (!mmc)
+               return -1;
+
+       if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size))
+               printf("\n\nCaution! Your devices Erase group is 0x%x\n"
+                       "The erase range would be change to 0x%lx~0x%lx\n\n",
+                      mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1),
+                      ((start + blkcnt + mmc->erase_grp_size)
+                      & ~(mmc->erase_grp_size - 1)) - 1);
+
+       while (blk < blkcnt) {
+               blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ?
+                       mmc->erase_grp_size : (blkcnt - blk);
+               err = mmc_erase_t(mmc, start + blk, blk_r);
+               if (err)
+                       break;
+
+               blk += blk_r;
+       }
+
+       return blk;
+}
+
 static ulong
 mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)
 {
@@ -449,6 +531,10 @@ int mmc_send_op_cond(struct mmc *mmc)
                                (mmc->voltages &
                                (cmd.response[0] & OCR_VOLTAGE_MASK)) |
                                (cmd.response[0] & OCR_ACCESS_MODE));
+
+               if (mmc->host_caps & MMC_MODE_HC)
+                       cmd.cmdarg |= OCR_HCS;
+
                cmd.flags = 0;
 
                err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -771,7 +857,7 @@ int mmc_startup(struct mmc *mmc)
 {
        int err;
        uint mult, freq;
-       u64 cmult, csize;
+       u64 cmult, csize, capacity;
        struct mmc_cmd cmd;
        char ext_csd[512];
        int timeout = 1000;
@@ -911,14 +997,40 @@ int mmc_startup(struct mmc *mmc)
                        return err;
        }
 
+       /*
+        * For SD, its erase group is always one sector
+        */
+       mmc->erase_grp_size = 1;
        mmc->part_config = MMCPART_NOAVAILABLE;
        if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
                /* check  ext_csd version and capacity */
                err = mmc_send_ext_csd(mmc, ext_csd);
                if (!err & (ext_csd[192] >= 2)) {
-                       mmc->capacity = ext_csd[212] << 0 | ext_csd[213] << 8 |
-                                       ext_csd[214] << 16 | ext_csd[215] << 24;
-                       mmc->capacity *= 512;
+                       /*
+                        * According to the JEDEC Standard, the value of
+                        * ext_csd's capacity is valid if the value is more
+                        * than 2GB
+                        */
+                       capacity = ext_csd[212] << 0 | ext_csd[213] << 8 |
+                                  ext_csd[214] << 16 | ext_csd[215] << 24;
+                       capacity *= 512;
+                       if ((capacity >> 20) > 2 * 1024)
+                               mmc->capacity = capacity;
+               }
+
+               /*
+                * Check whether GROUP_DEF is set, if yes, read out
+                * group size from ext_csd directly, or calculate
+                * the group size from the csd value.
+                */
+               if (ext_csd[175])
+                       mmc->erase_grp_size = ext_csd[224] * 512 * 1024;
+               else {
+                       int erase_gsz, erase_gmul;
+                       erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10;
+                       erase_gmul = (mmc->csd[2] & 0x000003e0) >> 5;
+                       mmc->erase_grp_size = (erase_gsz + 1)
+                               * (erase_gmul + 1);
                }
 
                /* store the partition info of emmc */
@@ -1044,6 +1156,7 @@ int mmc_register(struct mmc *mmc)
        mmc->block_dev.removable = 1;
        mmc->block_dev.block_read = mmc_bread;
        mmc->block_dev.block_write = mmc_bwrite;
+       mmc->block_dev.block_erase = mmc_berase;
        if (!mmc->b_max)
                mmc->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
 
diff --git a/drivers/mmc/mv_sdhci.c b/drivers/mmc/mv_sdhci.c
new file mode 100644 (file)
index 0000000..9e59951
--- /dev/null
@@ -0,0 +1,21 @@
+#include <common.h>
+#include <malloc.h>
+#include <sdhci.h>
+
+static char *MVSDH_NAME = "mv_sdh";
+int mv_sdh_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks)
+{
+       struct sdhci_host *host = NULL;
+       host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host));
+       if (!host) {
+               printf("sdh_host malloc fail!\n");
+               return 1;
+       }
+
+       host->name = MVSDH_NAME;
+       host->ioaddr = (void *)regbase;
+       host->quirks = quirks;
+       host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
+       add_sdhci(host, max_clk, min_clk);
+       return 0;
+}
index 280738fbf4eb191e2fe4ae29239c053214bda451..f1368132a3b2c53c461500747dc1dc4d708dab17 100644 (file)
@@ -462,7 +462,7 @@ static int s5p_mmc_initialize(int dev_index, int bus_width)
                mmc->host_caps = MMC_MODE_8BIT;
        else
                mmc->host_caps = MMC_MODE_4BIT;
-       mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+       mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC;
 
        mmc->f_min = 400000;
        mmc->f_max = 52000000;
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
new file mode 100644 (file)
index 0000000..9ebd33d
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2011, Marvell Semiconductor Inc.
+ * Lei Wen <leiwen@marvell.com>
+ *
+ * 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
+ *
+ * Back ported to the 8xx platform (from the 8260 platform) by
+ * Murray.Jensen@cmst.csiro.au, 27-Jan-01.
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <sdhci.h>
+
+void *aligned_buffer;
+
+static void sdhci_reset(struct sdhci_host *host, u8 mask)
+{
+       unsigned long timeout;
+
+       /* Wait max 100 ms */
+       timeout = 100;
+       sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
+       while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
+               if (timeout == 0) {
+                       printf("Reset 0x%x never completed.\n", (int)mask);
+                       return;
+               }
+               timeout--;
+               udelay(1000);
+       }
+}
+
+static void sdhci_cmd_done(struct sdhci_host *host, struct mmc_cmd *cmd)
+{
+       int i;
+       if (cmd->resp_type & MMC_RSP_136) {
+               /* CRC is stripped so we need to do some shifting. */
+               for (i = 0; i < 4; i++) {
+                       cmd->response[i] = sdhci_readl(host,
+                                       SDHCI_RESPONSE + (3-i)*4) << 8;
+                       if (i != 3)
+                               cmd->response[i] |= sdhci_readb(host,
+                                               SDHCI_RESPONSE + (3-i)*4-1);
+               }
+       } else {
+               cmd->response[0] = sdhci_readl(host, SDHCI_RESPONSE);
+       }
+}
+
+static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data)
+{
+       int i;
+       char *offs;
+       for (i = 0; i < data->blocksize; i += 4) {
+               offs = data->dest + i;
+               if (data->flags == MMC_DATA_READ)
+                       *(u32 *)offs = sdhci_readl(host, SDHCI_BUFFER);
+               else
+                       sdhci_writel(host, *(u32 *)offs, SDHCI_BUFFER);
+       }
+}
+
+static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data,
+                               unsigned int start_addr)
+{
+       unsigned int stat, rdy, mask, block = 0;
+
+       rdy = SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL;
+       mask = SDHCI_DATA_AVAILABLE | SDHCI_SPACE_AVAILABLE;
+       do {
+               stat = sdhci_readl(host, SDHCI_INT_STATUS);
+               if (stat & SDHCI_INT_ERROR) {
+                       printf("Error detected in status(0x%X)!\n", stat);
+                       return -1;
+               }
+               if (stat & rdy) {
+                       if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & mask))
+                               continue;
+                       sdhci_writel(host, rdy, SDHCI_INT_STATUS);
+                       sdhci_transfer_pio(host, data);
+                       data->dest += data->blocksize;
+                       if (++block >= data->blocks)
+                               break;
+               }
+#ifdef CONFIG_MMC_SDMA
+               if (stat & SDHCI_INT_DMA_END) {
+                       sdhci_writel(host, SDHCI_INT_DMA_END, SDHCI_INT_STATUS);
+                       start_addr &= SDHCI_DEFAULT_BOUNDARY_SIZE - 1;
+                       start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE;
+                       sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS);
+               }
+#endif
+       } while (!(stat & SDHCI_INT_DATA_END));
+       return 0;
+}
+
+int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
+                      struct mmc_data *data)
+{
+       struct sdhci_host *host = (struct sdhci_host *)mmc->priv;
+       unsigned int stat = 0;
+       int ret = 0;
+       int trans_bytes = 0, is_aligned = 1;
+       u32 mask, flags, mode;
+       unsigned int timeout, start_addr = 0;
+
+       /* Wait max 10 ms */
+       timeout = 10;
+
+       sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
+       mask = SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT;
+
+       /* We shouldn't wait for data inihibit for stop commands, even
+          though they might use busy signaling */
+       if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+               mask &= ~SDHCI_DATA_INHIBIT;
+
+       while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
+               if (timeout == 0) {
+                       printf("Controller never released inhibit bit(s).\n");
+                       return COMM_ERR;
+               }
+               timeout--;
+               udelay(1000);
+       }
+
+       mask = SDHCI_INT_RESPONSE;
+       if (!(cmd->resp_type & MMC_RSP_PRESENT))
+               flags = SDHCI_CMD_RESP_NONE;
+       else if (cmd->resp_type & MMC_RSP_136)
+               flags = SDHCI_CMD_RESP_LONG;
+       else if (cmd->resp_type & MMC_RSP_BUSY) {
+               flags = SDHCI_CMD_RESP_SHORT_BUSY;
+               mask |= SDHCI_INT_DATA_END;
+       } else
+               flags = SDHCI_CMD_RESP_SHORT;
+
+       if (cmd->resp_type & MMC_RSP_CRC)
+               flags |= SDHCI_CMD_CRC;
+       if (cmd->resp_type & MMC_RSP_OPCODE)
+               flags |= SDHCI_CMD_INDEX;
+       if (data)
+               flags |= SDHCI_CMD_DATA;
+
+       /*Set Transfer mode regarding to data flag*/
+       if (data != 0) {
+               sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
+               mode = SDHCI_TRNS_BLK_CNT_EN;
+               trans_bytes = data->blocks * data->blocksize;
+               if (data->blocks > 1)
+                       mode |= SDHCI_TRNS_MULTI;
+
+               if (data->flags == MMC_DATA_READ)
+                       mode |= SDHCI_TRNS_READ;
+
+#ifdef CONFIG_MMC_SDMA
+               if (data->flags == MMC_DATA_READ)
+                       start_addr = (unsigned int)data->dest;
+               else
+                       start_addr = (unsigned int)data->src;
+               if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
+                               (start_addr & 0x7) != 0x0) {
+                       is_aligned = 0;
+                       start_addr = (unsigned int)aligned_buffer;
+                       if (data->flags != MMC_DATA_READ)
+                               memcpy(aligned_buffer, data->src, trans_bytes);
+               }
+
+               sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS);
+               mode |= SDHCI_TRNS_DMA;
+#endif
+               sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
+                               data->blocksize),
+                               SDHCI_BLOCK_SIZE);
+               sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
+               sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
+       }
+
+       sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT);
+#ifdef CONFIG_MMC_SDMA
+       flush_cache(0, ~0);
+#endif
+       sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND);
+       do {
+               stat = sdhci_readl(host, SDHCI_INT_STATUS);
+               if (stat & SDHCI_INT_ERROR)
+                       break;
+       } while ((stat & mask) != mask);
+
+       if ((stat & (SDHCI_INT_ERROR | mask)) == mask) {
+               sdhci_cmd_done(host, cmd);
+               sdhci_writel(host, mask, SDHCI_INT_STATUS);
+       } else
+               ret = -1;
+
+       if (!ret && data)
+               ret = sdhci_transfer_data(host, data, start_addr);
+
+       stat = sdhci_readl(host, SDHCI_INT_STATUS);
+       sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_STATUS);
+       if (!ret) {
+               if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
+                               !is_aligned && (data->flags == MMC_DATA_READ))
+                       memcpy(data->dest, aligned_buffer, trans_bytes);
+               return 0;
+       }
+
+       sdhci_reset(host, SDHCI_RESET_CMD);
+       sdhci_reset(host, SDHCI_RESET_DATA);
+       if (stat & SDHCI_INT_TIMEOUT)
+               return TIMEOUT;
+       else
+               return COMM_ERR;
+}
+
+static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
+{
+       struct sdhci_host *host = (struct sdhci_host *)mmc->priv;
+       unsigned int div, clk, timeout;
+
+       sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+       if (clock == 0)
+               return 0;
+
+       if (host->version >= SDHCI_SPEC_300) {
+               /* Version 3.00 divisors must be a multiple of 2. */
+               if (mmc->f_max <= clock)
+                       div = 1;
+               else {
+                       for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
+                               if ((mmc->f_max / div) <= clock)
+                                       break;
+                       }
+               }
+       } else {
+               /* Version 2.00 divisors must be a power of 2. */
+               for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) {
+                       if ((mmc->f_max / div) <= clock)
+                               break;
+               }
+       }
+       div >>= 1;
+
+       clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
+       clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
+               << SDHCI_DIVIDER_HI_SHIFT;
+       clk |= SDHCI_CLOCK_INT_EN;
+       sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+       /* Wait max 20 ms */
+       timeout = 20;
+       while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+               & SDHCI_CLOCK_INT_STABLE)) {
+               if (timeout == 0) {
+                       printf("Internal clock never stabilised.\n");
+                       return -1;
+               }
+               timeout--;
+               udelay(1000);
+       }
+
+       clk |= SDHCI_CLOCK_CARD_EN;
+       sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+       return 0;
+}
+
+static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
+{
+       u8 pwr = 0;
+
+       if (power != (unsigned short)-1) {
+               switch (1 << power) {
+               case MMC_VDD_165_195:
+                       pwr = SDHCI_POWER_180;
+                       break;
+               case MMC_VDD_29_30:
+               case MMC_VDD_30_31:
+                       pwr = SDHCI_POWER_300;
+                       break;
+               case MMC_VDD_32_33:
+               case MMC_VDD_33_34:
+                       pwr = SDHCI_POWER_330;
+                       break;
+               }
+       }
+
+       if (pwr == 0) {
+               sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+               return;
+       }
+
+       pwr |= SDHCI_POWER_ON;
+
+       sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
+}
+
+void sdhci_set_ios(struct mmc *mmc)
+{
+       u32 ctrl;
+       struct sdhci_host *host = (struct sdhci_host *)mmc->priv;
+
+       if (mmc->clock != host->clock)
+               sdhci_set_clock(mmc, mmc->clock);
+
+       /* Set bus width */
+       ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+       if (mmc->bus_width == 8) {
+               ctrl &= ~SDHCI_CTRL_4BITBUS;
+               if (host->version >= SDHCI_SPEC_300)
+                       ctrl |= SDHCI_CTRL_8BITBUS;
+       } else {
+               if (host->version >= SDHCI_SPEC_300)
+                       ctrl &= ~SDHCI_CTRL_8BITBUS;
+               if (mmc->bus_width == 4)
+                       ctrl |= SDHCI_CTRL_4BITBUS;
+               else
+                       ctrl &= ~SDHCI_CTRL_4BITBUS;
+       }
+
+       if (mmc->clock > 26000000)
+               ctrl |= SDHCI_CTRL_HISPD;
+       else
+               ctrl &= ~SDHCI_CTRL_HISPD;
+
+       sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+}
+
+int sdhci_init(struct mmc *mmc)
+{
+       struct sdhci_host *host = (struct sdhci_host *)mmc->priv;
+
+       if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && !aligned_buffer) {
+               aligned_buffer = memalign(8, 512*1024);
+               if (!aligned_buffer) {
+                       printf("Aligned buffer alloc failed!!!");
+                       return -1;
+               }
+       }
+
+       /* Eable all state */
+       sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_INT_ENABLE);
+       sdhci_writel(host, SDHCI_INT_ALL_MASK, SDHCI_SIGNAL_ENABLE);
+
+       sdhci_set_power(host, fls(mmc->voltages) - 1);
+
+       return 0;
+}
+
+int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
+{
+       struct mmc *mmc;
+       unsigned int caps;
+
+       mmc = malloc(sizeof(struct mmc));
+       if (!mmc) {
+               printf("mmc malloc fail!\n");
+               return -1;
+       }
+
+       mmc->priv = host;
+
+       sprintf(mmc->name, "%s", host->name);
+       mmc->send_cmd = sdhci_send_command;
+       mmc->set_ios = sdhci_set_ios;
+       mmc->init = sdhci_init;
+
+       caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+#ifdef CONFIG_MMC_SDMA
+       if (!(caps & SDHCI_CAN_DO_SDMA)) {
+               printf("Your controller don't support sdma!!\n");
+               return -1;
+       }
+#endif
+
+       if (max_clk)
+               mmc->f_max = max_clk;
+       else {
+               if (host->version >= SDHCI_SPEC_300)
+                       mmc->f_max = (caps & SDHCI_CLOCK_V3_BASE_MASK)
+                               >> SDHCI_CLOCK_BASE_SHIFT;
+               else
+                       mmc->f_max = (caps & SDHCI_CLOCK_BASE_MASK)
+                               >> SDHCI_CLOCK_BASE_SHIFT;
+               mmc->f_max *= 1000000;
+       }
+       if (mmc->f_max == 0) {
+               printf("Hardware doesn't specify base clock frequency\n");
+               return -1;
+       }
+       if (min_clk)
+               mmc->f_min = min_clk;
+       else {
+               if (host->version >= SDHCI_SPEC_300)
+                       mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_300;
+               else
+                       mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_200;
+       }
+
+       mmc->voltages = 0;
+       if (caps & SDHCI_CAN_VDD_330)
+               mmc->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+       if (caps & SDHCI_CAN_VDD_300)
+               mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
+       if (caps & SDHCI_CAN_VDD_180)
+               mmc->voltages |= MMC_VDD_165_195;
+       mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT;
+       if (caps & SDHCI_CAN_DO_8BIT)
+               mmc->host_caps |= MMC_MODE_8BIT;
+
+       sdhci_reset(host, SDHCI_RESET_ALL);
+       mmc_register(mmc);
+
+       return 0;
+}
diff --git a/drivers/mmc/sh_mmcif.c b/drivers/mmc/sh_mmcif.c
new file mode 100644 (file)
index 0000000..567e2cb
--- /dev/null
@@ -0,0 +1,608 @@
+/*
+ * MMCIF driver.
+ *
+ * Copyright (C)  2011 Renesas Solutions Corp.
+ *
+ * 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.
+ */
+
+#include <config.h>
+#include <common.h>
+#include <watchdog.h>
+#include <command.h>
+#include <mmc.h>
+#include <malloc.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include "sh_mmcif.h"
+
+#define DRIVER_NAME    "sh_mmcif"
+
+static void *mmc_priv(struct mmc *mmc)
+{
+       return (void *)mmc->priv;
+}
+
+static int sh_mmcif_intr(void *dev_id)
+{
+       struct sh_mmcif_host *host = dev_id;
+       u32 state = 0;
+
+       state = sh_mmcif_read(&host->regs->ce_int);
+       state &= sh_mmcif_read(&host->regs->ce_int_mask);
+
+       if (state & INT_RBSYE) {
+               sh_mmcif_write(~(INT_RBSYE | INT_CRSPE), &host->regs->ce_int);
+               sh_mmcif_bitclr(MASK_MRBSYE, &host->regs->ce_int_mask);
+               goto end;
+       } else if (state & INT_CRSPE) {
+               sh_mmcif_write(~INT_CRSPE, &host->regs->ce_int);
+               sh_mmcif_bitclr(MASK_MCRSPE, &host->regs->ce_int_mask);
+               /* one more interrupt (INT_RBSYE) */
+               if (sh_mmcif_read(&host->regs->ce_cmd_set) & CMD_SET_RBSY)
+                       return -EAGAIN;
+               goto end;
+       } else if (state & INT_BUFREN) {
+               sh_mmcif_write(~INT_BUFREN, &host->regs->ce_int);
+               sh_mmcif_bitclr(MASK_MBUFREN, &host->regs->ce_int_mask);
+               goto end;
+       } else if (state & INT_BUFWEN) {
+               sh_mmcif_write(~INT_BUFWEN, &host->regs->ce_int);
+               sh_mmcif_bitclr(MASK_MBUFWEN, &host->regs->ce_int_mask);
+               goto end;
+       } else if (state & INT_CMD12DRE) {
+               sh_mmcif_write(~(INT_CMD12DRE | INT_CMD12RBE | INT_CMD12CRE |
+                                 INT_BUFRE), &host->regs->ce_int);
+               sh_mmcif_bitclr(MASK_MCMD12DRE, &host->regs->ce_int_mask);
+               goto end;
+       } else if (state & INT_BUFRE) {
+               sh_mmcif_write(~INT_BUFRE, &host->regs->ce_int);
+               sh_mmcif_bitclr(MASK_MBUFRE, &host->regs->ce_int_mask);
+               goto end;
+       } else if (state & INT_DTRANE) {
+               sh_mmcif_write(~INT_DTRANE, &host->regs->ce_int);
+               sh_mmcif_bitclr(MASK_MDTRANE, &host->regs->ce_int_mask);
+               goto end;
+       } else if (state & INT_CMD12RBE) {
+               sh_mmcif_write(~(INT_CMD12RBE | INT_CMD12CRE),
+                               &host->regs->ce_int);
+               sh_mmcif_bitclr(MASK_MCMD12RBE, &host->regs->ce_int_mask);
+               goto end;
+       } else if (state & INT_ERR_STS) {
+               /* err interrupts */
+               sh_mmcif_write(~state, &host->regs->ce_int);
+               sh_mmcif_bitclr(state, &host->regs->ce_int_mask);
+               goto err;
+       } else
+               return -EAGAIN;
+
+err:
+       host->sd_error = 1;
+       debug("%s: int err state = %08x\n", DRIVER_NAME, state);
+end:
+       host->wait_int = 1;
+       return 0;
+}
+
+static int mmcif_wait_interrupt_flag(struct sh_mmcif_host *host)
+{
+       int timeout = 10000000;
+
+       while (1) {
+               timeout--;
+               if (timeout < 0) {
+                       printf("timeout\n");
+                       return 0;
+               }
+
+               if (!sh_mmcif_intr(host))
+                       break;
+
+               udelay(1);      /* 1 usec */
+       }
+
+       return 1;       /* Return value: NOT 0 = complete waiting */
+}
+
+static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk)
+{
+       int i;
+
+       sh_mmcif_bitclr(CLK_ENABLE, &host->regs->ce_clk_ctrl);
+       sh_mmcif_bitclr(CLK_CLEAR, &host->regs->ce_clk_ctrl);
+
+       if (!clk)
+               return;
+       if (clk == CLKDEV_EMMC_DATA) {
+               sh_mmcif_bitset(CLK_PCLK, &host->regs->ce_clk_ctrl);
+       } else {
+               for (i = 1; (unsigned int)host->clk / (1 << i) >= clk; i++)
+                       ;
+               sh_mmcif_bitset((i - 1) << 16, &host->regs->ce_clk_ctrl);
+       }
+       sh_mmcif_bitset(CLK_ENABLE, &host->regs->ce_clk_ctrl);
+}
+
+static void sh_mmcif_sync_reset(struct sh_mmcif_host *host)
+{
+       u32 tmp;
+
+       tmp = sh_mmcif_read(&host->regs->ce_clk_ctrl) & (CLK_ENABLE |
+                                                        CLK_CLEAR);
+
+       sh_mmcif_write(SOFT_RST_ON, &host->regs->ce_version);
+       sh_mmcif_write(SOFT_RST_OFF, &host->regs->ce_version);
+       sh_mmcif_bitset(tmp | SRSPTO_256 | SRBSYTO_29 | SRWDTO_29 | SCCSTO_29,
+                       &host->regs->ce_clk_ctrl);
+       /* byte swap on */
+       sh_mmcif_bitset(BUF_ACC_ATYP, &host->regs->ce_buf_acc);
+}
+
+static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
+{
+       u32 state1, state2;
+       int ret, timeout = 10000000;
+
+       host->sd_error = 0;
+       host->wait_int = 0;
+
+       state1 = sh_mmcif_read(&host->regs->ce_host_sts1);
+       state2 = sh_mmcif_read(&host->regs->ce_host_sts2);
+       debug("%s: ERR HOST_STS1 = %08x\n", \
+                       DRIVER_NAME, sh_mmcif_read(&host->regs->ce_host_sts1));
+       debug("%s: ERR HOST_STS2 = %08x\n", \
+                       DRIVER_NAME, sh_mmcif_read(&host->regs->ce_host_sts2));
+
+       if (state1 & STS1_CMDSEQ) {
+               debug("%s: Forced end of command sequence\n", DRIVER_NAME);
+               sh_mmcif_bitset(CMD_CTRL_BREAK, &host->regs->ce_cmd_ctrl);
+               sh_mmcif_bitset(~CMD_CTRL_BREAK, &host->regs->ce_cmd_ctrl);
+               while (1) {
+                       timeout--;
+                       if (timeout < 0) {
+                               printf(DRIVER_NAME": Forceed end of " \
+                                       "command sequence timeout err\n");
+                               return -EILSEQ;
+                       }
+                       if (!(sh_mmcif_read(&host->regs->ce_host_sts1)
+                                                               & STS1_CMDSEQ))
+                               break;
+               }
+               sh_mmcif_sync_reset(host);
+               return -EILSEQ;
+       }
+
+       if (state2 & STS2_CRC_ERR)
+               ret = -EILSEQ;
+       else if (state2 & STS2_TIMEOUT_ERR)
+               ret = TIMEOUT;
+       else
+               ret = -EILSEQ;
+       return ret;
+}
+
+static int sh_mmcif_single_read(struct sh_mmcif_host *host,
+                               struct mmc_data *data)
+{
+       long time;
+       u32 blocksize, i;
+       unsigned long *p = (unsigned long *)data->dest;
+
+       if ((unsigned long)p & 0x00000001) {
+               printf("%s: The data pointer is unaligned.", __func__);
+               return -EIO;
+       }
+
+       host->wait_int = 0;
+
+       /* buf read enable */
+       sh_mmcif_bitset(MASK_MBUFREN, &host->regs->ce_int_mask);
+       time = mmcif_wait_interrupt_flag(host);
+       if (time == 0 || host->sd_error != 0)
+               return sh_mmcif_error_manage(host);
+
+       host->wait_int = 0;
+       blocksize = (BLOCK_SIZE_MASK &
+                       sh_mmcif_read(&host->regs->ce_block_set)) + 3;
+       for (i = 0; i < blocksize / 4; i++)
+               *p++ = sh_mmcif_read(&host->regs->ce_data);
+
+       /* buffer read end */
+       sh_mmcif_bitset(MASK_MBUFRE, &host->regs->ce_int_mask);
+       time = mmcif_wait_interrupt_flag(host);
+       if (time == 0 || host->sd_error != 0)
+               return sh_mmcif_error_manage(host);
+
+       host->wait_int = 0;
+       return 0;
+}
+
+static int sh_mmcif_multi_read(struct sh_mmcif_host *host,
+                               struct mmc_data *data)
+{
+       long time;
+       u32 blocksize, i, j;
+       unsigned long *p = (unsigned long *)data->dest;
+
+       if ((unsigned long)p & 0x00000001) {
+               printf("%s: The data pointer is unaligned.", __func__);
+               return -EIO;
+       }
+
+       host->wait_int = 0;
+       blocksize = BLOCK_SIZE_MASK & sh_mmcif_read(&host->regs->ce_block_set);
+       for (j = 0; j < data->blocks; j++) {
+               sh_mmcif_bitset(MASK_MBUFREN, &host->regs->ce_int_mask);
+               time = mmcif_wait_interrupt_flag(host);
+               if (time == 0 || host->sd_error != 0)
+                       return sh_mmcif_error_manage(host);
+
+               host->wait_int = 0;
+               for (i = 0; i < blocksize / 4; i++)
+                       *p++ = sh_mmcif_read(&host->regs->ce_data);
+
+               WATCHDOG_RESET();
+       }
+       return 0;
+}
+
+static int sh_mmcif_single_write(struct sh_mmcif_host *host,
+                                struct mmc_data *data)
+{
+       long time;
+       u32 blocksize, i;
+       const unsigned long *p = (unsigned long *)data->dest;
+
+       if ((unsigned long)p & 0x00000001) {
+               printf("%s: The data pointer is unaligned.", __func__);
+               return -EIO;
+       }
+
+       host->wait_int = 0;
+       sh_mmcif_bitset(MASK_MBUFWEN, &host->regs->ce_int_mask);
+
+       time = mmcif_wait_interrupt_flag(host);
+       if (time == 0 || host->sd_error != 0)
+               return sh_mmcif_error_manage(host);
+
+       host->wait_int = 0;
+       blocksize = (BLOCK_SIZE_MASK &
+                       sh_mmcif_read(&host->regs->ce_block_set)) + 3;
+       for (i = 0; i < blocksize / 4; i++)
+               sh_mmcif_write(*p++, &host->regs->ce_data);
+
+       /* buffer write end */
+       sh_mmcif_bitset(MASK_MDTRANE, &host->regs->ce_int_mask);
+
+       time = mmcif_wait_interrupt_flag(host);
+       if (time == 0 || host->sd_error != 0)
+               return sh_mmcif_error_manage(host);
+
+       host->wait_int = 0;
+       return 0;
+}
+
+static int sh_mmcif_multi_write(struct sh_mmcif_host *host,
+                               struct mmc_data *data)
+{
+       long time;
+       u32 i, j, blocksize;
+       const unsigned long *p = (unsigned long *)data->dest;
+
+       if ((unsigned long)p & 0x00000001) {
+               printf("%s: The data pointer is unaligned.", __func__);
+               return -EIO;
+       }
+
+       host->wait_int = 0;
+       blocksize = BLOCK_SIZE_MASK & sh_mmcif_read(&host->regs->ce_block_set);
+       for (j = 0; j < data->blocks; j++) {
+               sh_mmcif_bitset(MASK_MBUFWEN, &host->regs->ce_int_mask);
+
+               time = mmcif_wait_interrupt_flag(host);
+
+               if (time == 0 || host->sd_error != 0)
+                       return sh_mmcif_error_manage(host);
+
+               host->wait_int = 0;
+               for (i = 0; i < blocksize / 4; i++)
+                       sh_mmcif_write(*p++, &host->regs->ce_data);
+
+               WATCHDOG_RESET();
+       }
+       return 0;
+}
+
+static void sh_mmcif_get_response(struct sh_mmcif_host *host,
+                                       struct mmc_cmd *cmd)
+{
+       if (cmd->resp_type & MMC_RSP_136) {
+               cmd->response[0] = sh_mmcif_read(&host->regs->ce_resp3);
+               cmd->response[1] = sh_mmcif_read(&host->regs->ce_resp2);
+               cmd->response[2] = sh_mmcif_read(&host->regs->ce_resp1);
+               cmd->response[3] = sh_mmcif_read(&host->regs->ce_resp0);
+               debug(" RESP %08x, %08x, %08x, %08x\n", cmd->response[0],
+                        cmd->response[1], cmd->response[2], cmd->response[3]);
+       } else {
+               cmd->response[0] = sh_mmcif_read(&host->regs->ce_resp0);
+       }
+}
+
+static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host,
+                                       struct mmc_cmd *cmd)
+{
+       cmd->response[0] = sh_mmcif_read(&host->regs->ce_resp_cmd12);
+}
+
+static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
+                               struct mmc_data *data, struct mmc_cmd *cmd)
+{
+       u32 tmp = 0;
+       u32 opc = cmd->cmdidx;
+
+       /* Response Type check */
+       switch (cmd->resp_type) {
+       case MMC_RSP_NONE:
+               tmp |= CMD_SET_RTYP_NO;
+               break;
+       case MMC_RSP_R1:
+       case MMC_RSP_R1b:
+       case MMC_RSP_R3:
+               tmp |= CMD_SET_RTYP_6B;
+               break;
+       case MMC_RSP_R2:
+               tmp |= CMD_SET_RTYP_17B;
+               break;
+       default:
+               printf(DRIVER_NAME": Not support type response.\n");
+               break;
+       }
+
+       /* RBSY */
+       if (opc == MMC_CMD_SWITCH)
+               tmp |= CMD_SET_RBSY;
+
+       /* WDAT / DATW */
+       if (host->data) {
+               tmp |= CMD_SET_WDAT;
+               switch (host->bus_width) {
+               case MMC_BUS_WIDTH_1:
+                       tmp |= CMD_SET_DATW_1;
+                       break;
+               case MMC_BUS_WIDTH_4:
+                       tmp |= CMD_SET_DATW_4;
+                       break;
+               case MMC_BUS_WIDTH_8:
+                       tmp |= CMD_SET_DATW_8;
+                       break;
+               default:
+                       printf(DRIVER_NAME": Not support bus width.\n");
+                       break;
+               }
+       }
+       /* DWEN */
+       if (opc == MMC_CMD_WRITE_SINGLE_BLOCK ||
+           opc == MMC_CMD_WRITE_MULTIPLE_BLOCK)
+               tmp |= CMD_SET_DWEN;
+       /* CMLTE/CMD12EN */
+       if (opc == MMC_CMD_READ_MULTIPLE_BLOCK ||
+           opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) {
+               tmp |= CMD_SET_CMLTE | CMD_SET_CMD12EN;
+               sh_mmcif_bitset(data->blocks << 16, &host->regs->ce_block_set);
+       }
+       /* RIDXC[1:0] check bits */
+       if (opc == MMC_CMD_SEND_OP_COND || opc == MMC_CMD_ALL_SEND_CID ||
+           opc == MMC_CMD_SEND_CSD || opc == MMC_CMD_SEND_CID)
+               tmp |= CMD_SET_RIDXC_BITS;
+       /* RCRC7C[1:0] check bits */
+       if (opc == MMC_CMD_SEND_OP_COND)
+               tmp |= CMD_SET_CRC7C_BITS;
+       /* RCRC7C[1:0] internal CRC7 */
+       if (opc == MMC_CMD_ALL_SEND_CID ||
+               opc == MMC_CMD_SEND_CSD || opc == MMC_CMD_SEND_CID)
+               tmp |= CMD_SET_CRC7C_INTERNAL;
+
+       return opc = ((opc << 24) | tmp);
+}
+
+static u32 sh_mmcif_data_trans(struct sh_mmcif_host *host,
+                               struct mmc_data *data, u16 opc)
+{
+       u32 ret;
+
+       switch (opc) {
+       case MMC_CMD_READ_MULTIPLE_BLOCK:
+               ret = sh_mmcif_multi_read(host, data);
+               break;
+       case MMC_CMD_WRITE_MULTIPLE_BLOCK:
+               ret = sh_mmcif_multi_write(host, data);
+               break;
+       case MMC_CMD_WRITE_SINGLE_BLOCK:
+               ret = sh_mmcif_single_write(host, data);
+               break;
+       case MMC_CMD_READ_SINGLE_BLOCK:
+       case MMC_CMD_SEND_EXT_CSD:
+               ret = sh_mmcif_single_read(host, data);
+               break;
+       default:
+               printf(DRIVER_NAME": NOT SUPPORT CMD = d'%08d\n", opc);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int sh_mmcif_start_cmd(struct sh_mmcif_host *host,
+                               struct mmc_data *data, struct mmc_cmd *cmd)
+{
+       long time;
+       int ret = 0, mask = 0;
+       u32 opc = cmd->cmdidx;
+
+       if (opc == MMC_CMD_STOP_TRANSMISSION) {
+               /* MMCIF sends the STOP command automatically */
+               if (host->last_cmd == MMC_CMD_READ_MULTIPLE_BLOCK)
+                       sh_mmcif_bitset(MASK_MCMD12DRE,
+                                       &host->regs->ce_int_mask);
+               else
+                       sh_mmcif_bitset(MASK_MCMD12RBE,
+                                       &host->regs->ce_int_mask);
+
+               time = mmcif_wait_interrupt_flag(host);
+               if (time == 0 || host->sd_error != 0)
+                       return sh_mmcif_error_manage(host);
+
+               sh_mmcif_get_cmd12response(host, cmd);
+               return 0;
+       }
+       if (opc == MMC_CMD_SWITCH)
+               mask = MASK_MRBSYE;
+       else
+               mask = MASK_MCRSPE;
+
+       mask |= MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR |
+               MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR |
+               MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO |
+               MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO;
+
+       if (host->data) {
+               sh_mmcif_write(0, &host->regs->ce_block_set);
+               sh_mmcif_write(data->blocksize, &host->regs->ce_block_set);
+       }
+       opc = sh_mmcif_set_cmd(host, data, cmd);
+
+       sh_mmcif_write(INT_START_MAGIC, &host->regs->ce_int);
+       sh_mmcif_write(mask, &host->regs->ce_int_mask);
+
+       debug("CMD%d ARG:%08x\n", cmd->cmdidx, cmd->cmdarg);
+       /* set arg */
+       sh_mmcif_write(cmd->cmdarg, &host->regs->ce_arg);
+       host->wait_int = 0;
+       /* set cmd */
+       sh_mmcif_write(opc, &host->regs->ce_cmd_set);
+
+       time = mmcif_wait_interrupt_flag(host);
+       if (time == 0)
+               return sh_mmcif_error_manage(host);
+
+       if (host->sd_error) {
+               switch (cmd->cmdidx) {
+               case MMC_CMD_ALL_SEND_CID:
+               case MMC_CMD_SELECT_CARD:
+               case MMC_CMD_APP_CMD:
+                       ret = TIMEOUT;
+                       break;
+               default:
+                       printf(DRIVER_NAME": Cmd(d'%d) err\n", cmd->cmdidx);
+                       ret = sh_mmcif_error_manage(host);
+                       break;
+               }
+               host->sd_error = 0;
+               host->wait_int = 0;
+               return ret;
+       }
+
+       /* if no response */
+       if (!(opc & 0x00C00000))
+               return 0;
+
+       if (host->wait_int == 1) {
+               sh_mmcif_get_response(host, cmd);
+               host->wait_int = 0;
+       }
+       if (host->data)
+               ret = sh_mmcif_data_trans(host, data, cmd->cmdidx);
+       host->last_cmd = cmd->cmdidx;
+
+       return ret;
+}
+
+static int sh_mmcif_request(struct mmc *mmc, struct mmc_cmd *cmd,
+                           struct mmc_data *data)
+{
+       struct sh_mmcif_host *host = mmc_priv(mmc);
+       int ret;
+
+       WATCHDOG_RESET();
+
+       switch (cmd->cmdidx) {
+       case MMC_CMD_APP_CMD:
+               return TIMEOUT;
+       case MMC_CMD_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */
+               if (data)
+                       /* ext_csd */
+                       break;
+               else
+                       /* send_if_cond cmd (not support) */
+                       return TIMEOUT;
+       default:
+               break;
+       }
+       host->sd_error = 0;
+       host->data = data;
+       ret = sh_mmcif_start_cmd(host, data, cmd);
+       host->data = NULL;
+
+       return ret;
+}
+
+static void sh_mmcif_set_ios(struct mmc *mmc)
+{
+       struct sh_mmcif_host *host = mmc_priv(mmc);
+
+       if (mmc->clock)
+               sh_mmcif_clock_control(host, mmc->clock);
+
+       if (mmc->bus_width == 8)
+               host->bus_width = MMC_BUS_WIDTH_8;
+       else if (mmc->bus_width == 4)
+               host->bus_width = MMC_BUS_WIDTH_4;
+       else
+               host->bus_width = MMC_BUS_WIDTH_1;
+
+       debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width);
+}
+
+static int sh_mmcif_init(struct mmc *mmc)
+{
+       struct sh_mmcif_host *host = mmc_priv(mmc);
+
+       sh_mmcif_sync_reset(host);
+       sh_mmcif_write(MASK_ALL, &host->regs->ce_int_mask);
+       return 0;
+}
+
+int mmcif_mmc_init(void)
+{
+       int ret = 0;
+       struct mmc *mmc;
+       struct sh_mmcif_host *host = NULL;
+
+       mmc = malloc(sizeof(struct mmc));
+       if (!mmc)
+               ret = -ENOMEM;
+       memset(mmc, 0, sizeof(*mmc));
+       host = malloc(sizeof(struct sh_mmcif_host));
+       if (!host)
+               ret = -ENOMEM;
+       memset(host, 0, sizeof(*host));
+
+       mmc->f_min = CLKDEV_MMC_INIT;
+       mmc->f_max = CLKDEV_EMMC_DATA;
+       mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+       mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT |
+                        MMC_MODE_8BIT;
+       memcpy(mmc->name, DRIVER_NAME, sizeof(DRIVER_NAME));
+       mmc->send_cmd = sh_mmcif_request;
+       mmc->set_ios = sh_mmcif_set_ios;
+       mmc->init = sh_mmcif_init;
+       host->regs = (struct sh_mmcif_regs *)CONFIG_SH_MMCIF_ADDR;
+       host->clk = CONFIG_SH_MMCIF_CLK;
+       mmc->priv = host;
+
+       mmc_register(mmc);
+
+       return ret;
+}
diff --git a/drivers/mmc/sh_mmcif.h b/drivers/mmc/sh_mmcif.h
new file mode 100644 (file)
index 0000000..bd6fbf7
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * MMCIF driver.
+ *
+ * Copyright (C)  2011 Renesas Solutions Corp.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _SH_MMCIF_H_
+#define _SH_MMCIF_H_
+
+struct sh_mmcif_regs {
+       unsigned long ce_cmd_set;
+       unsigned long reserved;
+       unsigned long ce_arg;
+       unsigned long ce_arg_cmd12;
+       unsigned long ce_cmd_ctrl;
+       unsigned long ce_block_set;
+       unsigned long ce_clk_ctrl;
+       unsigned long ce_buf_acc;
+       unsigned long ce_resp3;
+       unsigned long ce_resp2;
+       unsigned long ce_resp1;
+       unsigned long ce_resp0;
+       unsigned long ce_resp_cmd12;
+       unsigned long ce_data;
+       unsigned long reserved2[2];
+       unsigned long ce_int;
+       unsigned long ce_int_mask;
+       unsigned long ce_host_sts1;
+       unsigned long ce_host_sts2;
+       unsigned long reserved3[11];
+       unsigned long ce_version;
+};
+
+/* CE_CMD_SET */
+#define CMD_MASK               0x3f000000
+#define CMD_SET_RTYP_NO                ((0 << 23) | (0 << 22))
+/* R1/R1b/R3/R4/R5 */
+#define CMD_SET_RTYP_6B                ((0 << 23) | (1 << 22))
+/* R2 */
+#define CMD_SET_RTYP_17B       ((1 << 23) | (0 << 22))
+/* R1b */
+#define CMD_SET_RBSY           (1 << 21)
+#define CMD_SET_CCSEN          (1 << 20)
+/* 1: on data, 0: no data */
+#define CMD_SET_WDAT           (1 << 19)
+/* 1: write to card, 0: read from card */
+#define CMD_SET_DWEN           (1 << 18)
+/* 1: multi block trans, 0: single */
+#define CMD_SET_CMLTE          (1 << 17)
+/* 1: CMD12 auto issue */
+#define CMD_SET_CMD12EN                (1 << 16)
+/* index check */
+#define CMD_SET_RIDXC_INDEX    ((0 << 15) | (0 << 14))
+/* check bits check */
+#define CMD_SET_RIDXC_BITS     ((0 << 15) | (1 << 14))
+/* no check */
+#define CMD_SET_RIDXC_NO       ((1 << 15) | (0 << 14))
+/* 1: CRC7 check*/
+#define CMD_SET_CRC7C          ((0 << 13) | (0 << 12))
+/* 1: check bits check*/
+#define CMD_SET_CRC7C_BITS     ((0 << 13) | (1 << 12))
+/* 1: internal CRC7 check*/
+#define CMD_SET_CRC7C_INTERNAL ((1 << 13) | (0 << 12))
+/* 1: CRC16 check*/
+#define CMD_SET_CRC16C         (1 << 10)
+/* 1: not receive CRC status */
+#define CMD_SET_CRCSTE         (1 << 8)
+/* 1: tran mission bit "Low" */
+#define CMD_SET_TBIT           (1 << 7)
+/* 1: open/drain */
+#define CMD_SET_OPDM           (1 << 6)
+#define CMD_SET_CCSH           (1 << 5)
+/* 1bit */
+#define CMD_SET_DATW_1         ((0 << 1) | (0 << 0))
+/* 4bit */
+#define CMD_SET_DATW_4         ((0 << 1) | (1 << 0))
+/* 8bit */
+#define CMD_SET_DATW_8         ((1 << 1) | (0 << 0))
+
+/* CE_CMD_CTRL */
+#define CMD_CTRL_BREAK         (1 << 0)
+
+/* CE_BLOCK_SET */
+#define BLOCK_SIZE_MASK                0x0000ffff
+
+/* CE_CLK_CTRL */
+#define CLK_ENABLE             (1 << 24)
+#define CLK_CLEAR              ((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16))
+#define CLK_PCLK               ((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16))
+/* respons timeout */
+#define SRSPTO_256             ((1 << 13) | (0 << 12))
+/* respons busy timeout */
+#define SRBSYTO_29             ((1 << 11) | (1 << 10) | (1 << 9) | (1 << 8))
+/* read/write timeout */
+#define SRWDTO_29              ((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4))
+/* ccs timeout */
+#define SCCSTO_29              ((1 << 3) | (1 << 2) | (1 << 1) | (1 << 0))
+
+/* CE_BUF_ACC */
+#define BUF_ACC_DMAWEN         (1 << 25)
+#define BUF_ACC_DMAREN         (1 << 24)
+#define BUF_ACC_BUSW_32                (0 << 17)
+#define BUF_ACC_BUSW_16                (1 << 17)
+#define BUF_ACC_ATYP           (1 << 16)
+
+/* CE_INT */
+#define INT_CCSDE              (1 << 29)
+#define INT_CMD12DRE           (1 << 26)
+#define INT_CMD12RBE           (1 << 25)
+#define INT_CMD12CRE           (1 << 24)
+#define INT_DTRANE             (1 << 23)
+#define INT_BUFRE              (1 << 22)
+#define INT_BUFWEN             (1 << 21)
+#define INT_BUFREN             (1 << 20)
+#define INT_CCSRCV             (1 << 19)
+#define INT_RBSYE              (1 << 17)
+#define INT_CRSPE              (1 << 16)
+#define INT_CMDVIO             (1 << 15)
+#define INT_BUFVIO             (1 << 14)
+#define INT_WDATERR            (1 << 11)
+#define INT_RDATERR            (1 << 10)
+#define INT_RIDXERR            (1 << 9)
+#define INT_RSPERR             (1 << 8)
+#define INT_CCSTO              (1 << 5)
+#define INT_CRCSTO             (1 << 4)
+#define INT_WDATTO             (1 << 3)
+#define INT_RDATTO             (1 << 2)
+#define INT_RBSYTO             (1 << 1)
+#define INT_RSPTO              (1 << 0)
+#define INT_ERR_STS            (INT_CMDVIO | INT_BUFVIO | INT_WDATERR |  \
+                                INT_RDATERR | INT_RIDXERR | INT_RSPERR | \
+                                INT_CCSTO | INT_CRCSTO | INT_WDATTO |    \
+                                INT_RDATTO | INT_RBSYTO | INT_RSPTO)
+#define INT_START_MAGIC                0xD80430C0
+
+/* CE_INT_MASK */
+#define MASK_ALL               0x00000000
+#define MASK_MCCSDE            (1 << 29)
+#define MASK_MCMD12DRE         (1 << 26)
+#define MASK_MCMD12RBE         (1 << 25)
+#define MASK_MCMD12CRE         (1 << 24)
+#define MASK_MDTRANE           (1 << 23)
+#define MASK_MBUFRE            (1 << 22)
+#define MASK_MBUFWEN           (1 << 21)
+#define MASK_MBUFREN           (1 << 20)
+#define MASK_MCCSRCV           (1 << 19)
+#define MASK_MRBSYE            (1 << 17)
+#define MASK_MCRSPE            (1 << 16)
+#define MASK_MCMDVIO           (1 << 15)
+#define MASK_MBUFVIO           (1 << 14)
+#define MASK_MWDATERR          (1 << 11)
+#define MASK_MRDATERR          (1 << 10)
+#define MASK_MRIDXERR          (1 << 9)
+#define MASK_MRSPERR           (1 << 8)
+#define MASK_MCCSTO            (1 << 5)
+#define MASK_MCRCSTO           (1 << 4)
+#define MASK_MWDATTO           (1 << 3)
+#define MASK_MRDATTO           (1 << 2)
+#define MASK_MRBSYTO           (1 << 1)
+#define MASK_MRSPTO            (1 << 0)
+
+/* CE_HOST_STS1 */
+#define STS1_CMDSEQ            (1 << 31)
+
+/* CE_HOST_STS2 */
+#define STS2_CRCSTE            (1 << 31)
+#define STS2_CRC16E            (1 << 30)
+#define STS2_AC12CRCE          (1 << 29)
+#define STS2_RSPCRC7E          (1 << 28)
+#define STS2_CRCSTEBE          (1 << 27)
+#define STS2_RDATEBE           (1 << 26)
+#define STS2_AC12REBE          (1 << 25)
+#define STS2_RSPEBE            (1 << 24)
+#define STS2_AC12IDXE          (1 << 23)
+#define STS2_RSPIDXE           (1 << 22)
+#define STS2_CCSTO             (1 << 15)
+#define STS2_RDATTO            (1 << 14)
+#define STS2_DATBSYTO          (1 << 13)
+#define STS2_CRCSTTO           (1 << 12)
+#define STS2_AC12BSYTO         (1 << 11)
+#define STS2_RSPBSYTO          (1 << 10)
+#define STS2_AC12RSPTO         (1 << 9)
+#define STS2_RSPTO             (1 << 8)
+
+#define STS2_CRC_ERR           (STS2_CRCSTE | STS2_CRC16E |            \
+                                STS2_AC12CRCE | STS2_RSPCRC7E | STS2_CRCSTEBE)
+#define STS2_TIMEOUT_ERR       (STS2_CCSTO | STS2_RDATTO |             \
+                                STS2_DATBSYTO | STS2_CRCSTTO |         \
+                                STS2_AC12BSYTO | STS2_RSPBSYTO |       \
+                                STS2_AC12RSPTO | STS2_RSPTO)
+
+/* CE_VERSION */
+#define SOFT_RST_ON            (1 << 31)
+#define SOFT_RST_OFF           (0 << 31)
+
+#define CLKDEV_EMMC_DATA       52000000        /* 52MHz */
+#define        CLKDEV_MMC_INIT         400000          /* 100 - 400 KHz */
+
+#define MMC_BUS_WIDTH_1                0
+#define MMC_BUS_WIDTH_4                2
+#define MMC_BUS_WIDTH_8                3
+
+struct sh_mmcif_host {
+       struct mmc_data         *data;
+       struct sh_mmcif_regs    *regs;
+       unsigned int            clk;
+       int                     bus_width;
+       u16                     wait_int;
+       u16                     sd_error;
+       u8                      last_cmd;
+};
+
+static inline u32 sh_mmcif_read(unsigned long *reg)
+{
+       return readl(reg);
+}
+
+static inline void sh_mmcif_write(u32 val, unsigned long *reg)
+{
+       writel(val, reg);
+}
+
+static inline void sh_mmcif_bitset(u32 val, unsigned long *reg)
+{
+       sh_mmcif_write(val | sh_mmcif_read(reg), reg);
+}
+
+static inline void sh_mmcif_bitclr(u32 val, unsigned long *reg)
+{
+       sh_mmcif_write(~val & sh_mmcif_read(reg), reg);
+}
+
+#endif /* _SH_MMCIF_H_ */
diff --git a/drivers/mmc/tegra2_mmc.c b/drivers/mmc/tegra2_mmc.c
new file mode 100644 (file)
index 0000000..8b6f829
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * (C) Copyright 2009 SAMSUNG Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ * Jaehoon Chung <jh80.chung@samsung.com>
+ * Portions Copyright 2011 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <common.h>
+#include <mmc.h>
+#include <asm/io.h>
+#include <asm/arch/clk_rst.h>
+#include "tegra2_mmc.h"
+
+/* support 4 mmc hosts */
+struct mmc mmc_dev[4];
+struct mmc_host mmc_host[4];
+
+static inline struct tegra2_mmc *tegra2_get_base_mmc(int dev_index)
+{
+       unsigned long offset;
+       debug("tegra2_get_base_mmc: dev_index = %d\n", dev_index);
+
+       switch (dev_index) {
+       case 0:
+               offset = TEGRA2_SDMMC4_BASE;
+               break;
+       case 1:
+               offset = TEGRA2_SDMMC3_BASE;
+               break;
+       case 2:
+               offset = TEGRA2_SDMMC2_BASE;
+               break;
+       case 3:
+               offset = TEGRA2_SDMMC1_BASE;
+               break;
+       default:
+               offset = TEGRA2_SDMMC4_BASE;
+               break;
+       }
+
+       return (struct tegra2_mmc *)(offset);
+}
+
+static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data)
+{
+       unsigned char ctrl;
+
+       debug("data->dest: %08X, data->blocks: %u, data->blocksize: %u\n",
+       (u32)data->dest, data->blocks, data->blocksize);
+
+       writel((u32)data->dest, &host->reg->sysad);
+       /*
+        * DMASEL[4:3]
+        * 00 = Selects SDMA
+        * 01 = Reserved
+        * 10 = Selects 32-bit Address ADMA2
+        * 11 = Selects 64-bit Address ADMA2
+        */
+       ctrl = readb(&host->reg->hostctl);
+       ctrl &= ~(3 << 3);                      /* SDMA */
+       writeb(ctrl, &host->reg->hostctl);
+
+       /* We do not handle DMA boundaries, so set it to max (512 KiB) */
+       writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize);
+       writew(data->blocks, &host->reg->blkcnt);
+}
+
+static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data)
+{
+       unsigned short mode;
+       debug(" mmc_set_transfer_mode called\n");
+       /*
+        * TRNMOD
+        * MUL1SIN0[5]  : Multi/Single Block Select
+        * RD1WT0[4]    : Data Transfer Direction Select
+        *      1 = read
+        *      0 = write
+        * ENACMD12[2]  : Auto CMD12 Enable
+        * ENBLKCNT[1]  : Block Count Enable
+        * ENDMA[0]     : DMA Enable
+        */
+       mode = (1 << 1) | (1 << 0);
+       if (data->blocks > 1)
+               mode |= (1 << 5);
+       if (data->flags & MMC_DATA_READ)
+               mode |= (1 << 4);
+
+       writew(mode, &host->reg->trnmod);
+}
+
+static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+                       struct mmc_data *data)
+{
+       struct mmc_host *host = (struct mmc_host *)mmc->priv;
+       int flags, i;
+       unsigned int timeout;
+       unsigned int mask;
+       unsigned int retry = 0x100000;
+       debug(" mmc_send_cmd called\n");
+
+       /* Wait max 10 ms */
+       timeout = 10;
+
+       /*
+        * PRNSTS
+        * CMDINHDAT[1] : Command Inhibit (DAT)
+        * CMDINHCMD[0] : Command Inhibit (CMD)
+        */
+       mask = (1 << 0);
+       if ((data != NULL) || (cmd->resp_type & MMC_RSP_BUSY))
+               mask |= (1 << 1);
+
+       /*
+        * We shouldn't wait for data inhibit for stop commands, even
+        * though they might use busy signaling
+        */
+       if (data)
+               mask &= ~(1 << 1);
+
+       while (readl(&host->reg->prnsts) & mask) {
+               if (timeout == 0) {
+                       printf("%s: timeout error\n", __func__);
+                       return -1;
+               }
+               timeout--;
+               udelay(1000);
+       }
+
+       if (data)
+               mmc_prepare_data(host, data);
+
+       debug("cmd->arg: %08x\n", cmd->cmdarg);
+       writel(cmd->cmdarg, &host->reg->argument);
+
+       if (data)
+               mmc_set_transfer_mode(host, data);
+
+       if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY))
+               return -1;
+
+       /*
+        * CMDREG
+        * CMDIDX[13:8] : Command index
+        * DATAPRNT[5]  : Data Present Select
+        * ENCMDIDX[4]  : Command Index Check Enable
+        * ENCMDCRC[3]  : Command CRC Check Enable
+        * RSPTYP[1:0]
+        *      00 = No Response
+        *      01 = Length 136
+        *      10 = Length 48
+        *      11 = Length 48 Check busy after response
+        */
+       if (!(cmd->resp_type & MMC_RSP_PRESENT))
+               flags = 0;
+       else if (cmd->resp_type & MMC_RSP_136)
+               flags = (1 << 0);
+       else if (cmd->resp_type & MMC_RSP_BUSY)
+               flags = (3 << 0);
+       else
+               flags = (2 << 0);
+
+       if (cmd->resp_type & MMC_RSP_CRC)
+               flags |= (1 << 3);
+       if (cmd->resp_type & MMC_RSP_OPCODE)
+               flags |= (1 << 4);
+       if (data)
+               flags |= (1 << 5);
+
+       debug("cmd: %d\n", cmd->cmdidx);
+
+       writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg);
+
+       for (i = 0; i < retry; i++) {
+               mask = readl(&host->reg->norintsts);
+               /* Command Complete */
+               if (mask & (1 << 0)) {
+                       if (!data)
+                               writel(mask, &host->reg->norintsts);
+                       break;
+               }
+       }
+
+       if (i == retry) {
+               printf("%s: waiting for status update\n", __func__);
+               return TIMEOUT;
+       }
+
+       if (mask & (1 << 16)) {
+               /* Timeout Error */
+               debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx);
+               return TIMEOUT;
+       } else if (mask & (1 << 15)) {
+               /* Error Interrupt */
+               debug("error: %08x cmd %d\n", mask, cmd->cmdidx);
+               return -1;
+       }
+
+       if (cmd->resp_type & MMC_RSP_PRESENT) {
+               if (cmd->resp_type & MMC_RSP_136) {
+                       /* CRC is stripped so we need to do some shifting. */
+                       for (i = 0; i < 4; i++) {
+                               unsigned int offset =
+                                       (unsigned int)(&host->reg->rspreg3 - i);
+                               cmd->response[i] = readl(offset) << 8;
+
+                               if (i != 3) {
+                                       cmd->response[i] |=
+                                               readb(offset - 1);
+                               }
+                               debug("cmd->resp[%d]: %08x\n",
+                                               i, cmd->response[i]);
+                       }
+               } else if (cmd->resp_type & MMC_RSP_BUSY) {
+                       for (i = 0; i < retry; i++) {
+                               /* PRNTDATA[23:20] : DAT[3:0] Line Signal */
+                               if (readl(&host->reg->prnsts)
+                                       & (1 << 20))    /* DAT[0] */
+                                       break;
+                       }
+
+                       if (i == retry) {
+                               printf("%s: card is still busy\n", __func__);
+                               return TIMEOUT;
+                       }
+
+                       cmd->response[0] = readl(&host->reg->rspreg0);
+                       debug("cmd->resp[0]: %08x\n", cmd->response[0]);
+               } else {
+                       cmd->response[0] = readl(&host->reg->rspreg0);
+                       debug("cmd->resp[0]: %08x\n", cmd->response[0]);
+               }
+       }
+
+       if (data) {
+               while (1) {
+                       mask = readl(&host->reg->norintsts);
+
+                       if (mask & (1 << 15)) {
+                               /* Error Interrupt */
+                               writel(mask, &host->reg->norintsts);
+                               printf("%s: error during transfer: 0x%08x\n",
+                                               __func__, mask);
+                               return -1;
+                       } else if (mask & (1 << 3)) {
+                               /* DMA Interrupt */
+                               debug("DMA end\n");
+                               break;
+                       } else if (mask & (1 << 1)) {
+                               /* Transfer Complete */
+                               debug("r/w is done\n");
+                               break;
+                       }
+               }
+               writel(mask, &host->reg->norintsts);
+       }
+
+       udelay(1000);
+       return 0;
+}
+
+static void mmc_change_clock(struct mmc_host *host, uint clock)
+{
+       int div, hw_div;
+       unsigned short clk;
+       unsigned long timeout;
+       unsigned int reg, hostbase;
+       struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+       debug(" mmc_change_clock called\n");
+
+       /* Change Tegra2 SDMMCx clock divisor here */
+       /* Source is 216MHz, PLLP_OUT0 */
+       if (clock == 0)
+               goto out;
+
+       div = 1;
+       if (clock <= 400000) {
+               hw_div = ((9-1)<<1);            /* Best match is 375KHz */
+               div = 64;
+       } else if (clock <= 20000000)
+               hw_div = ((11-1)<<1);           /* Best match is 19.6MHz */
+       else if (clock <= 26000000)
+               hw_div = ((9-1)<<1);            /* Use 24MHz */
+       else
+               hw_div = ((4-1)<<1) + 1;        /* 4.5 divisor for 48MHz */
+
+       debug("mmc_change_clock: hw_div = %d, card clock div = %d\n",
+               hw_div, div);
+
+       /* Change SDMMCx divisor */
+
+       hostbase = readl(&host->base);
+       debug("mmc_change_clock: hostbase = %08X\n", hostbase);
+
+       if (hostbase == TEGRA2_SDMMC1_BASE) {
+               reg = readl(&clkrst->crc_clk_src_sdmmc1);
+               reg &= 0xFFFFFF00;      /* divisor (7.1) = 00 */
+               reg |= hw_div;          /* n-1 */
+               writel(reg, &clkrst->crc_clk_src_sdmmc1);
+       } else if (hostbase == TEGRA2_SDMMC2_BASE) {
+               reg = readl(&clkrst->crc_clk_src_sdmmc2);
+               reg &= 0xFFFFFF00;      /* divisor (7.1) = 00 */
+               reg |= hw_div;          /* n-1 */
+               writel(reg, &clkrst->crc_clk_src_sdmmc2);
+       } else if (hostbase == TEGRA2_SDMMC3_BASE) {
+               reg = readl(&clkrst->crc_clk_src_sdmmc3);
+               reg &= 0xFFFFFF00;      /* divisor (7.1) = 00 */
+               reg |= hw_div;          /* n-1 */
+               writel(reg, &clkrst->crc_clk_src_sdmmc3);
+       } else {
+               reg = readl(&clkrst->crc_clk_src_sdmmc4);
+               reg &= 0xFFFFFF00;      /* divisor (7.1) = 00 */
+               reg |= hw_div;          /* n-1 */
+               writel(reg, &clkrst->crc_clk_src_sdmmc4);
+       }
+
+       writew(0, &host->reg->clkcon);
+
+       div >>= 1;
+       /*
+        * CLKCON
+        * SELFREQ[15:8]        : base clock divided by value
+        * ENSDCLK[2]           : SD Clock Enable
+        * STBLINTCLK[1]        : Internal Clock Stable
+        * ENINTCLK[0]          : Internal Clock Enable
+        */
+       clk = (div << 8) | (1 << 0);
+       writew(clk, &host->reg->clkcon);
+
+       /* Wait max 10 ms */
+       timeout = 10;
+       while (!(readw(&host->reg->clkcon) & (1 << 1))) {
+               if (timeout == 0) {
+                       printf("%s: timeout error\n", __func__);
+                       return;
+               }
+               timeout--;
+               udelay(1000);
+       }
+
+       clk |= (1 << 2);
+       writew(clk, &host->reg->clkcon);
+
+       debug("mmc_change_clock: clkcon = %08X\n", clk);
+       debug("mmc_change_clock: CLK_SOURCE_SDMMCx = %08X\n", reg);
+
+out:
+       host->clock = clock;
+}
+
+static void mmc_set_ios(struct mmc *mmc)
+{
+       struct mmc_host *host = mmc->priv;
+       unsigned char ctrl;
+       debug(" mmc_set_ios called\n");
+
+       debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
+
+       /* Change clock first */
+
+       mmc_change_clock(host, mmc->clock);
+
+       ctrl = readb(&host->reg->hostctl);
+
+       /*
+        * WIDE8[5]
+        * 0 = Depend on WIDE4
+        * 1 = 8-bit mode
+        * WIDE4[1]
+        * 1 = 4-bit mode
+        * 0 = 1-bit mode
+        */
+       if (mmc->bus_width == 8)
+               ctrl |= (1 << 5);
+       else if (mmc->bus_width == 4)
+               ctrl |= (1 << 1);
+       else
+               ctrl &= ~(1 << 1);
+
+       writeb(ctrl, &host->reg->hostctl);
+       debug("mmc_set_ios: hostctl = %08X\n", ctrl);
+}
+
+static void mmc_reset(struct mmc_host *host)
+{
+       unsigned int timeout;
+       debug(" mmc_reset called\n");
+
+       /*
+        * RSTALL[0] : Software reset for all
+        * 1 = reset
+        * 0 = work
+        */
+       writeb((1 << 0), &host->reg->swrst);
+
+       host->clock = 0;
+
+       /* Wait max 100 ms */
+       timeout = 100;
+
+       /* hw clears the bit when it's done */
+       while (readb(&host->reg->swrst) & (1 << 0)) {
+               if (timeout == 0) {
+                       printf("%s: timeout error\n", __func__);
+                       return;
+               }
+               timeout--;
+               udelay(1000);
+       }
+}
+
+static int mmc_core_init(struct mmc *mmc)
+{
+       struct mmc_host *host = (struct mmc_host *)mmc->priv;
+       unsigned int mask;
+       debug(" mmc_core_init called\n");
+
+       mmc_reset(host);
+
+       host->version = readw(&host->reg->hcver);
+       debug("host version = %x\n", host->version);
+
+       /* mask all */
+       writel(0xffffffff, &host->reg->norintstsen);
+       writel(0xffffffff, &host->reg->norintsigen);
+
+       writeb(0xe, &host->reg->timeoutcon);    /* TMCLK * 2^27 */
+       /*
+        * NORMAL Interrupt Status Enable Register init
+        * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable
+        * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable
+        * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable
+        * [0] ENSTACMDCMPLT : Command Complete Status Enable
+       */
+       mask = readl(&host->reg->norintstsen);
+       mask &= ~(0xffff);
+       mask |= (1 << 5) | (1 << 4) | (1 << 1) | (1 << 0);
+       writel(mask, &host->reg->norintstsen);
+
+       /*
+        * NORMAL Interrupt Signal Enable Register init
+        * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable
+        */
+       mask = readl(&host->reg->norintsigen);
+       mask &= ~(0xffff);
+       mask |= (1 << 1);
+       writel(mask, &host->reg->norintsigen);
+
+       return 0;
+}
+
+static int tegra2_mmc_initialize(int dev_index, int bus_width)
+{
+       struct mmc *mmc;
+
+       debug(" mmc_initialize called\n");
+
+       mmc = &mmc_dev[dev_index];
+
+       sprintf(mmc->name, "Tegra2 SD/MMC");
+       mmc->priv = &mmc_host[dev_index];
+       mmc->send_cmd = mmc_send_cmd;
+       mmc->set_ios = mmc_set_ios;
+       mmc->init = mmc_core_init;
+
+       mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+       if (bus_width == 8)
+               mmc->host_caps = MMC_MODE_8BIT;
+       else
+               mmc->host_caps = MMC_MODE_4BIT;
+       mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+
+       /*
+        * min freq is for card identification, and is the highest
+        *  low-speed SDIO card frequency (actually 400KHz)
+        * max freq is highest HS eMMC clock as per the SD/MMC spec
+        *  (actually 52MHz)
+        * Both of these are the closest equivalents w/216MHz source
+        *  clock and Tegra2 SDMMC divisors.
+        */
+       mmc->f_min = 375000;
+       mmc->f_max = 48000000;
+
+       mmc_host[dev_index].clock = 0;
+       mmc_host[dev_index].reg = tegra2_get_base_mmc(dev_index);
+       mmc_host[dev_index].base = (unsigned int)mmc_host[dev_index].reg;
+       mmc_register(mmc);
+
+       return 0;
+}
+
+int tegra2_mmc_init(int dev_index, int bus_width)
+{
+       debug(" tegra2_mmc_init: index %d, bus width %d\n",
+               dev_index, bus_width);
+       return tegra2_mmc_initialize(dev_index, bus_width);
+}
diff --git a/drivers/mmc/tegra2_mmc.h b/drivers/mmc/tegra2_mmc.h
new file mode 100644 (file)
index 0000000..4b80f9f
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * (C) Copyright 2009 SAMSUNG Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ * Portions Copyright (C) 2011 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef __TEGRA2_MMC_H_
+#define __TEGRA2_MMC_H_
+
+#define TEGRA2_SDMMC1_BASE     0xC8000000
+#define TEGRA2_SDMMC2_BASE     0xC8000200
+#define TEGRA2_SDMMC3_BASE     0xC8000400
+#define TEGRA2_SDMMC4_BASE     0xC8000600
+
+#ifndef __ASSEMBLY__
+struct tegra2_mmc {
+       unsigned int    sysad;          /* _SYSTEM_ADDRESS_0 */
+       unsigned short  blksize;        /* _BLOCK_SIZE_BLOCK_COUNT_0 15:00 */
+       unsigned short  blkcnt;         /* _BLOCK_SIZE_BLOCK_COUNT_0 31:16 */
+       unsigned int    argument;       /* _ARGUMENT_0 */
+       unsigned short  trnmod;         /* _CMD_XFER_MODE_0 15:00 xfer mode */
+       unsigned short  cmdreg;         /* _CMD_XFER_MODE_0 31:16 cmd reg */
+       unsigned int    rspreg0;        /* _RESPONSE_R0_R1_0 CMD RESP 31:00 */
+       unsigned int    rspreg1;        /* _RESPONSE_R2_R3_0 CMD RESP 63:32 */
+       unsigned int    rspreg2;        /* _RESPONSE_R4_R5_0 CMD RESP 95:64 */
+       unsigned int    rspreg3;        /* _RESPONSE_R6_R7_0 CMD RESP 127:96 */
+       unsigned int    bdata;          /* _BUFFER_DATA_PORT_0 */
+       unsigned int    prnsts;         /* _PRESENT_STATE_0 */
+       unsigned char   hostctl;        /* _POWER_CONTROL_HOST_0 7:00 */
+       unsigned char   pwrcon;         /* _POWER_CONTROL_HOST_0 15:8 */
+       unsigned char   blkgap;         /* _POWER_CONTROL_HOST_9 23:16 */
+       unsigned char   wakcon;         /* _POWER_CONTROL_HOST_0 31:24 */
+       unsigned short  clkcon;         /* _CLOCK_CONTROL_0 15:00 */
+       unsigned char   timeoutcon;     /* _TIMEOUT_CTRL 23:16 */
+       unsigned char   swrst;          /* _SW_RESET_ 31:24 */
+       unsigned int    norintsts;      /* _INTERRUPT_STATUS_0 */
+       unsigned int    norintstsen;    /* _INTERRUPT_STATUS_ENABLE_0 */
+       unsigned int    norintsigen;    /* _INTERRUPT_SIGNAL_ENABLE_0 */
+       unsigned short  acmd12errsts;   /* _AUTO_CMD12_ERR_STATUS_0 15:00 */
+       unsigned char   res1[2];        /* _RESERVED 31:16 */
+       unsigned int    capareg;        /* _CAPABILITIES_0 */
+       unsigned char   res2[4];        /* RESERVED, offset 44h-47h */
+       unsigned int    maxcurr;        /* _MAXIMUM_CURRENT_0 */
+       unsigned char   res3[4];        /* RESERVED, offset 4Ch-4Fh */
+       unsigned short  setacmd12err;   /* offset 50h */
+       unsigned short  setinterr;      /* offset 52h */
+       unsigned char   admaerr;        /* offset 54h */
+       unsigned char   res4[3];        /* RESERVED, offset 55h-57h */
+       unsigned long   admaaddr;       /* offset 58h-5Fh */
+       unsigned char   res5[0x9c];     /* RESERVED, offset 60h-FBh */
+       unsigned short  slotintstatus;  /* offset FCh */
+       unsigned short  hcver;          /* HOST Version */
+       unsigned char   res6[0x100];    /* RESERVED, offset 100h-1FFh */
+};
+
+struct mmc_host {
+       struct tegra2_mmc *reg;
+       unsigned int version;   /* SDHCI spec. version */
+       unsigned int clock;     /* Current clock (MHz) */
+       unsigned int base;      /* Base address, SDMMC1/2/3/4 */
+};
+
+int tegra2_mmc_init(int dev_index, int bus_width);
+
+#endif /* __ASSEMBLY__ */
+#endif /* __TEGRA2_MMC_H_ */
index 34bd899174b3a61cff43891d6458edf55f05c043..89e4911244c1052ae84359552e6ec3dad3ff8386 100644 (file)
 #define CONFIG_SYS_BOARD_ODMDATA       0x300d8011 /* lp1, 1GB */
 
 #define CONFIG_BOARD_EARLY_INIT_F
+
+/* SD/MMC */
+#define CONFIG_MMC
+#define CONFIG_GENERIC_MMC
+#define CONFIG_TEGRA2_MMC
+#define CONFIG_CMD_MMC
+
+#define CONFIG_DOS_PARTITION
+#define CONFIG_EFI_PARTITION
+#define CONFIG_CMD_EXT2
+#define CONFIG_CMD_FAT
 #endif /* __CONFIG_H */
index 06ce3e2b7c88d969cce0cd6baaed06f0c2fed15c..7d2914472a6974cd9d7251a1fac71ea72b9cc5a8 100644 (file)
 #define CONFIG_SYS_BOARD_ODMDATA       0x300d8011 /* lp1, 1GB */
 
 #define CONFIG_BOARD_EARLY_INIT_F
+
+/* SD/MMC */
+#define CONFIG_MMC
+#define CONFIG_GENERIC_MMC
+#define CONFIG_TEGRA2_MMC
+#define CONFIG_CMD_MMC
+
+#define CONFIG_DOS_PARTITION
+#define CONFIG_EFI_PARTITION
+#define CONFIG_CMD_EXT2
+#define CONFIG_CMD_FAT
 #endif /* __CONFIG_H */
index aeacdee3095d8a5356b791b11944ea54587800dd..53aff9b4b485005006254c3c6eeee127da93a230 100644 (file)
@@ -45,6 +45,7 @@
 #define MMC_MODE_4BIT          0x100
 #define MMC_MODE_8BIT          0x200
 #define MMC_MODE_SPI           0x400
+#define MMC_MODE_HC            0x800
 
 #define SD_DATA_4BIT   0x00040000
 
@@ -75,6 +76,9 @@
 #define MMC_CMD_READ_MULTIPLE_BLOCK    18
 #define MMC_CMD_WRITE_SINGLE_BLOCK     24
 #define MMC_CMD_WRITE_MULTIPLE_BLOCK   25
+#define MMC_CMD_ERASE_GROUP_START      35
+#define MMC_CMD_ERASE_GROUP_END                36
+#define MMC_CMD_ERASE                  38
 #define MMC_CMD_APP_CMD                        55
 #define MMC_CMD_SPI_READ_OCR           58
 #define MMC_CMD_SPI_CRC_ON_OFF         59
@@ -84,6 +88,8 @@
 #define SD_CMD_SEND_IF_COND            8
 
 #define SD_CMD_APP_SET_BUS_WIDTH       6
+#define SD_CMD_ERASE_WR_BLK_START      32
+#define SD_CMD_ERASE_WR_BLK_END                33
 #define SD_CMD_APP_SEND_OP_COND                41
 #define SD_CMD_APP_SEND_SCR            51
 
 #define OCR_VOLTAGE_MASK       0x007FFF80
 #define OCR_ACCESS_MODE                0x60000000
 
+#define SECURE_ERASE           0x80000000
+
 #define MMC_STATUS_MASK                (~0x0206BF7F)
 #define MMC_STATUS_RDY_FOR_DATA (1 << 8)
 #define MMC_STATUS_CURR_STATE  (0xf << 9)
@@ -285,6 +293,7 @@ struct mmc {
        uint tran_speed;
        uint read_bl_len;
        uint write_bl_len;
+       uint erase_grp_size;
        u64 capacity;
        block_dev_desc_t block_dev;
        int (*send_cmd)(struct mmc *mmc,
index 3cdae0214eeaae747257bc9b92c1d9abe391d9ec..524351182904bdef5e01aa2fce55c3760f9f564f 100644 (file)
@@ -49,6 +49,9 @@ typedef struct block_dev_desc {
                                       unsigned long start,
                                       lbaint_t blkcnt,
                                       const void *buffer);
+       unsigned long   (*block_erase)(int dev,
+                                      unsigned long start,
+                                      lbaint_t blkcnt);
        void            *priv;          /* driver private struct pointer */
 }block_dev_desc_t;
 
diff --git a/include/sdhci.h b/include/sdhci.h
new file mode 100644 (file)
index 0000000..6d52ce9
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2011, Marvell Semiconductor Inc.
+ * Lei Wen <leiwen@marvell.com>
+ *
+ * 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
+ *
+ * Back ported to the 8xx platform (from the 8260 platform) by
+ * Murray.Jensen@cmst.csiro.au, 27-Jan-01.
+ */
+#ifndef __SDHCI_HW_H
+#define __SDHCI_HW_H
+
+#include <asm/io.h>
+/*
+ * Controller registers
+ */
+
+#define SDHCI_DMA_ADDRESS      0x00
+
+#define SDHCI_BLOCK_SIZE       0x04
+#define  SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
+
+#define SDHCI_BLOCK_COUNT      0x06
+
+#define SDHCI_ARGUMENT         0x08
+
+#define SDHCI_TRANSFER_MODE    0x0C
+#define  SDHCI_TRNS_DMA                0x01
+#define  SDHCI_TRNS_BLK_CNT_EN 0x02
+#define  SDHCI_TRNS_ACMD12     0x04
+#define  SDHCI_TRNS_READ       0x10
+#define  SDHCI_TRNS_MULTI      0x20
+
+#define SDHCI_COMMAND          0x0E
+#define  SDHCI_CMD_RESP_MASK   0x03
+#define  SDHCI_CMD_CRC         0x08
+#define  SDHCI_CMD_INDEX       0x10
+#define  SDHCI_CMD_DATA                0x20
+#define  SDHCI_CMD_ABORTCMD    0xC0
+
+#define  SDHCI_CMD_RESP_NONE   0x00
+#define  SDHCI_CMD_RESP_LONG   0x01
+#define  SDHCI_CMD_RESP_SHORT  0x02
+#define  SDHCI_CMD_RESP_SHORT_BUSY 0x03
+
+#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
+#define SDHCI_GET_CMD(c) ((c>>8) & 0x3f)
+
+#define SDHCI_RESPONSE         0x10
+
+#define SDHCI_BUFFER           0x20
+
+#define SDHCI_PRESENT_STATE    0x24
+#define  SDHCI_CMD_INHIBIT     0x00000001
+#define  SDHCI_DATA_INHIBIT    0x00000002
+#define  SDHCI_DOING_WRITE     0x00000100
+#define  SDHCI_DOING_READ      0x00000200
+#define  SDHCI_SPACE_AVAILABLE 0x00000400
+#define  SDHCI_DATA_AVAILABLE  0x00000800
+#define  SDHCI_CARD_PRESENT    0x00010000
+#define  SDHCI_WRITE_PROTECT   0x00080000
+
+#define SDHCI_HOST_CONTROL     0x28
+#define  SDHCI_CTRL_LED                0x01
+#define  SDHCI_CTRL_4BITBUS    0x02
+#define  SDHCI_CTRL_HISPD      0x04
+#define  SDHCI_CTRL_DMA_MASK   0x18
+#define   SDHCI_CTRL_SDMA      0x00
+#define   SDHCI_CTRL_ADMA1     0x08
+#define   SDHCI_CTRL_ADMA32    0x10
+#define   SDHCI_CTRL_ADMA64    0x18
+#define   SDHCI_CTRL_8BITBUS   0x20
+
+#define SDHCI_POWER_CONTROL    0x29
+#define  SDHCI_POWER_ON                0x01
+#define  SDHCI_POWER_180       0x0A
+#define  SDHCI_POWER_300       0x0C
+#define  SDHCI_POWER_330       0x0E
+
+#define SDHCI_BLOCK_GAP_CONTROL        0x2A
+
+#define SDHCI_WAKE_UP_CONTROL  0x2B
+#define  SDHCI_WAKE_ON_INT     0x01
+#define  SDHCI_WAKE_ON_INSERT  0x02
+#define  SDHCI_WAKE_ON_REMOVE  0x04
+
+#define SDHCI_CLOCK_CONTROL    0x2C
+#define  SDHCI_DIVIDER_SHIFT   8
+#define  SDHCI_DIVIDER_HI_SHIFT        6
+#define  SDHCI_DIV_MASK        0xFF
+#define  SDHCI_DIV_MASK_LEN    8
+#define  SDHCI_DIV_HI_MASK     0x300
+#define  SDHCI_CLOCK_CARD_EN   0x0004
+#define  SDHCI_CLOCK_INT_STABLE        0x0002
+#define  SDHCI_CLOCK_INT_EN    0x0001
+
+#define SDHCI_TIMEOUT_CONTROL  0x2E
+
+#define SDHCI_SOFTWARE_RESET   0x2F
+#define  SDHCI_RESET_ALL       0x01
+#define  SDHCI_RESET_CMD       0x02
+#define  SDHCI_RESET_DATA      0x04
+
+#define SDHCI_INT_STATUS       0x30
+#define SDHCI_INT_ENABLE       0x34
+#define SDHCI_SIGNAL_ENABLE    0x38
+#define  SDHCI_INT_RESPONSE    0x00000001
+#define  SDHCI_INT_DATA_END    0x00000002
+#define  SDHCI_INT_DMA_END     0x00000008
+#define  SDHCI_INT_SPACE_AVAIL 0x00000010
+#define  SDHCI_INT_DATA_AVAIL  0x00000020
+#define  SDHCI_INT_CARD_INSERT 0x00000040
+#define  SDHCI_INT_CARD_REMOVE 0x00000080
+#define  SDHCI_INT_CARD_INT    0x00000100
+#define  SDHCI_INT_ERROR       0x00008000
+#define  SDHCI_INT_TIMEOUT     0x00010000
+#define  SDHCI_INT_CRC         0x00020000
+#define  SDHCI_INT_END_BIT     0x00040000
+#define  SDHCI_INT_INDEX       0x00080000
+#define  SDHCI_INT_DATA_TIMEOUT        0x00100000
+#define  SDHCI_INT_DATA_CRC    0x00200000
+#define  SDHCI_INT_DATA_END_BIT        0x00400000
+#define  SDHCI_INT_BUS_POWER   0x00800000
+#define  SDHCI_INT_ACMD12ERR   0x01000000
+#define  SDHCI_INT_ADMA_ERROR  0x02000000
+
+#define  SDHCI_INT_NORMAL_MASK 0x00007FFF
+#define  SDHCI_INT_ERROR_MASK  0xFFFF8000
+
+#define  SDHCI_INT_CMD_MASK    (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
+               SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
+#define  SDHCI_INT_DATA_MASK   (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
+               SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
+               SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
+               SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR)
+#define SDHCI_INT_ALL_MASK     ((unsigned int)-1)
+
+#define SDHCI_ACMD12_ERR       0x3C
+
+/* 3E-3F reserved */
+
+#define SDHCI_CAPABILITIES     0x40
+#define  SDHCI_TIMEOUT_CLK_MASK        0x0000003F
+#define  SDHCI_TIMEOUT_CLK_SHIFT 0
+#define  SDHCI_TIMEOUT_CLK_UNIT        0x00000080
+#define  SDHCI_CLOCK_BASE_MASK 0x00003F00
+#define  SDHCI_CLOCK_V3_BASE_MASK      0x0000FF00
+#define  SDHCI_CLOCK_BASE_SHIFT        8
+#define  SDHCI_MAX_BLOCK_MASK  0x00030000
+#define  SDHCI_MAX_BLOCK_SHIFT  16
+#define  SDHCI_CAN_DO_8BIT     0x00040000
+#define  SDHCI_CAN_DO_ADMA2    0x00080000
+#define  SDHCI_CAN_DO_ADMA1    0x00100000
+#define  SDHCI_CAN_DO_HISPD    0x00200000
+#define  SDHCI_CAN_DO_SDMA     0x00400000
+#define  SDHCI_CAN_VDD_330     0x01000000
+#define  SDHCI_CAN_VDD_300     0x02000000
+#define  SDHCI_CAN_VDD_180     0x04000000
+#define  SDHCI_CAN_64BIT       0x10000000
+
+#define SDHCI_CAPABILITIES_1   0x44
+
+#define SDHCI_MAX_CURRENT      0x48
+
+/* 4C-4F reserved for more max current */
+
+#define SDHCI_SET_ACMD12_ERROR 0x50
+#define SDHCI_SET_INT_ERROR    0x52
+
+#define SDHCI_ADMA_ERROR       0x54
+
+/* 55-57 reserved */
+
+#define SDHCI_ADMA_ADDRESS     0x58
+
+/* 60-FB reserved */
+
+#define SDHCI_SLOT_INT_STATUS  0xFC
+
+#define SDHCI_HOST_VERSION     0xFE
+#define  SDHCI_VENDOR_VER_MASK 0xFF00
+#define  SDHCI_VENDOR_VER_SHIFT        8
+#define  SDHCI_SPEC_VER_MASK   0x00FF
+#define  SDHCI_SPEC_VER_SHIFT  0
+#define   SDHCI_SPEC_100       0
+#define   SDHCI_SPEC_200       1
+#define   SDHCI_SPEC_300       2
+
+/*
+ * End of controller registers.
+ */
+
+#define SDHCI_MAX_DIV_SPEC_200 256
+#define SDHCI_MAX_DIV_SPEC_300 2046
+
+/*
+ * quirks
+ */
+#define SDHCI_QUIRK_32BIT_DMA_ADDR     (1 << 0)
+
+/*
+ * Host SDMA buffer boundary. Valid values from 4K to 512K in powers of 2.
+ */
+#define SDHCI_DEFAULT_BOUNDARY_SIZE    (512 * 1024)
+#define SDHCI_DEFAULT_BOUNDARY_ARG     (7)
+struct sdhci_ops {
+#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
+       u32             (*read_l)(struct sdhci_host *host, int reg);
+       u16             (*read_w)(struct sdhci_host *host, int reg);
+       u8              (*read_b)(struct sdhci_host *host, int reg);
+       void            (*write_l)(struct sdhci_host *host, u32 val, int reg);
+       void            (*write_w)(struct sdhci_host *host, u16 val, int reg);
+       void            (*write_b)(struct sdhci_host *host, u8 val, int reg);
+#endif
+};
+
+struct sdhci_host {
+       char *name;
+       void *ioaddr;
+       unsigned int quirks;
+       unsigned int version;
+       unsigned int clock;
+       const struct sdhci_ops *ops;
+};
+
+#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
+
+static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
+{
+       if (unlikely(host->ops->write_l))
+               host->ops->write_l(host, val, reg);
+       else
+               writel(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
+{
+       if (unlikely(host->ops->write_w))
+               host->ops->write_w(host, val, reg);
+       else
+               writew(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+       if (unlikely(host->ops->write_b))
+               host->ops->write_b(host, val, reg);
+       else
+               writeb(val, host->ioaddr + reg);
+}
+
+static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
+{
+       if (unlikely(host->ops->read_l))
+               return host->ops->read_l(host, reg);
+       else
+               return readl(host->ioaddr + reg);
+}
+
+static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
+{
+       if (unlikely(host->ops->read_w))
+               return host->ops->read_w(host, reg);
+       else
+               return readw(host->ioaddr + reg);
+}
+
+static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
+{
+       if (unlikely(host->ops->read_b))
+               return host->ops->read_b(host, reg);
+       else
+               return readb(host->ioaddr + reg);
+}
+
+#else
+
+static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
+{
+       writel(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
+{
+       writew(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+       writeb(val, host->ioaddr + reg);
+}
+static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
+{
+       return readl(host->ioaddr + reg);
+}
+
+static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
+{
+       return readw(host->ioaddr + reg);
+}
+
+static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
+{
+       return readb(host->ioaddr + reg);
+}
+#endif
+
+int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk);
+#endif /* __SDHCI_HW_H */