summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <trini@ti.com>2015-01-20 15:21:36 (GMT)
committerTom Rini <trini@ti.com>2015-01-20 15:21:36 (GMT)
commit1cd20006989532c0a688870a43727ace58997785 (patch)
treefc4ac0ad47bbce444fcea5c16b7df97595970270
parentb44a414959e3a32576a243aaa4966c1a9565ccfa (diff)
parentfc5b32fbf3bd43dabaf409e424e0bfb9a1b4df26 (diff)
downloadu-boot-fsl-qoriq-1cd20006989532c0a688870a43727ace58997785.tar.xz
Merge branch 'master' of git://git.denx.de/u-boot-mmc
-rw-r--r--arch/arm/include/asm/arch-rmobile/r8a7790.h6
-rw-r--r--arch/arm/include/asm/arch-rmobile/r8a7791.h5
-rw-r--r--arch/arm/include/asm/arch-rmobile/r8a7793.h5
-rw-r--r--arch/arm/include/asm/arch-rmobile/r8a7794.h5
-rw-r--r--arch/arm/include/asm/arch-rmobile/rcar-base.h3
-rw-r--r--arch/arm/include/asm/arch-rmobile/sh_sdhi.h168
-rw-r--r--common/cmd_mmc.c205
-rw-r--r--drivers/mmc/Kconfig9
-rw-r--r--drivers/mmc/Makefile1
-rw-r--r--drivers/mmc/mmc.c307
-rw-r--r--drivers/mmc/sh_sdhi.c695
-rw-r--r--include/mmc.h45
12 files changed, 1427 insertions, 27 deletions
diff --git a/arch/arm/include/asm/arch-rmobile/r8a7790.h b/arch/arm/include/asm/arch-rmobile/r8a7790.h
index 132d58c..748b802 100644
--- a/arch/arm/include/asm/arch-rmobile/r8a7790.h
+++ b/arch/arm/include/asm/arch-rmobile/r8a7790.h
@@ -28,6 +28,12 @@
#define MSTP10_BITS 0xFFFEFFE0
#define MSTP11_BITS 0x00000000
+/* SDHI */
+#define CONFIG_SYS_SH_SDHI1_BASE 0xEE120000
+#define CONFIG_SYS_SH_SDHI2_BASE 0xEE140000
+#define CONFIG_SYS_SH_SDHI3_BASE 0xEE160000
+#define CONFIG_SYS_SH_SDHI_NR_CHANNEL 4
+
#define R8A7790_CUT_ES2X 2
#define IS_R8A7790_ES2() \
(rmobile_get_cpu_rev_integer() == R8A7790_CUT_ES2X)
diff --git a/arch/arm/include/asm/arch-rmobile/r8a7791.h b/arch/arm/include/asm/arch-rmobile/r8a7791.h
index d2cbcd7..1d06b65 100644
--- a/arch/arm/include/asm/arch-rmobile/r8a7791.h
+++ b/arch/arm/include/asm/arch-rmobile/r8a7791.h
@@ -17,6 +17,11 @@
/* SH-I2C */
#define CONFIG_SYS_I2C_SH_BASE2 0xE60B0000
+/* SDHI */
+#define CONFIG_SYS_SH_SDHI1_BASE 0xEE140000
+#define CONFIG_SYS_SH_SDHI2_BASE 0xEE160000
+#define CONFIG_SYS_SH_SDHI_NR_CHANNEL 3
+
#define DBSC3_1_QOS_R0_BASE 0xE67A1000
#define DBSC3_1_QOS_R1_BASE 0xE67A1100
#define DBSC3_1_QOS_R2_BASE 0xE67A1200
diff --git a/arch/arm/include/asm/arch-rmobile/r8a7793.h b/arch/arm/include/asm/arch-rmobile/r8a7793.h
index 1abdeb7..3efc62a 100644
--- a/arch/arm/include/asm/arch-rmobile/r8a7793.h
+++ b/arch/arm/include/asm/arch-rmobile/r8a7793.h
@@ -18,6 +18,11 @@
/* SH-I2C */
#define CONFIG_SYS_I2C_SH_BASE2 0xE60B0000
+/* SDHI */
+#define CONFIG_SYS_SH_SDHI1_BASE 0xEE140000
+#define CONFIG_SYS_SH_SDHI2_BASE 0xEE160000
+#define CONFIG_SYS_SH_SDHI_NR_CHANNEL 3
+
#define DBSC3_1_QOS_R0_BASE 0xE67A1000
#define DBSC3_1_QOS_R1_BASE 0xE67A1100
#define DBSC3_1_QOS_R2_BASE 0xE67A1200
diff --git a/arch/arm/include/asm/arch-rmobile/r8a7794.h b/arch/arm/include/asm/arch-rmobile/r8a7794.h
index d7c9004..6d11fa4 100644
--- a/arch/arm/include/asm/arch-rmobile/r8a7794.h
+++ b/arch/arm/include/asm/arch-rmobile/r8a7794.h
@@ -27,4 +27,9 @@
#define MSTP10_BITS 0xFFFEFFE0
#define MSTP11_BITS 0x000001C0
+/* SDHI */
+#define CONFIG_SYS_SH_SDHI1_BASE 0xEE140000
+#define CONFIG_SYS_SH_SDHI2_BASE 0xEE160000
+#define CONFIG_SYS_SH_SDHI_NR_CHANNEL 3
+
#endif /* __ASM_ARCH_R8A7794_H */
diff --git a/arch/arm/include/asm/arch-rmobile/rcar-base.h b/arch/arm/include/asm/arch-rmobile/rcar-base.h
index 23c4bba..d594cd7 100644
--- a/arch/arm/include/asm/arch-rmobile/rcar-base.h
+++ b/arch/arm/include/asm/arch-rmobile/rcar-base.h
@@ -82,6 +82,9 @@
#define CONFIG_SYS_RCAR_I2C2_BASE 0xE6530000
#define CONFIG_SYS_RCAR_I2C3_BASE 0xE6540000
+/* SDHI */
+#define CONFIG_SYS_SH_SDHI0_BASE 0xEE100000
+
#define S3C_BASE 0xE6784000
#define S3C_INT_BASE 0xE6784A00
#define S3C_MEDIA_BASE 0xE6784B00
diff --git a/arch/arm/include/asm/arch-rmobile/sh_sdhi.h b/arch/arm/include/asm/arch-rmobile/sh_sdhi.h
new file mode 100644
index 0000000..057bf3f
--- /dev/null
+++ b/arch/arm/include/asm/arch-rmobile/sh_sdhi.h
@@ -0,0 +1,168 @@
+/*
+ * drivers/mmc/sh-sdhi.h
+ *
+ * SD/MMC driver for Reneas rmobile ARM SoCs
+ *
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2008-2009 Renesas Solutions Corp.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef _SH_SDHI_H
+#define _SH_SDHI_H
+
+#define SDHI_CMD (0x0000 >> 1)
+#define SDHI_PORTSEL (0x0004 >> 1)
+#define SDHI_ARG0 (0x0008 >> 1)
+#define SDHI_ARG1 (0x000C >> 1)
+#define SDHI_STOP (0x0010 >> 1)
+#define SDHI_SECCNT (0x0014 >> 1)
+#define SDHI_RSP00 (0x0018 >> 1)
+#define SDHI_RSP01 (0x001C >> 1)
+#define SDHI_RSP02 (0x0020 >> 1)
+#define SDHI_RSP03 (0x0024 >> 1)
+#define SDHI_RSP04 (0x0028 >> 1)
+#define SDHI_RSP05 (0x002C >> 1)
+#define SDHI_RSP06 (0x0030 >> 1)
+#define SDHI_RSP07 (0x0034 >> 1)
+#define SDHI_INFO1 (0x0038 >> 1)
+#define SDHI_INFO2 (0x003C >> 1)
+#define SDHI_INFO1_MASK (0x0040 >> 1)
+#define SDHI_INFO2_MASK (0x0044 >> 1)
+#define SDHI_CLK_CTRL (0x0048 >> 1)
+#define SDHI_SIZE (0x004C >> 1)
+#define SDHI_OPTION (0x0050 >> 1)
+#define SDHI_ERR_STS1 (0x0058 >> 1)
+#define SDHI_ERR_STS2 (0x005C >> 1)
+#define SDHI_BUF0 (0x0060 >> 1)
+#define SDHI_SDIO_MODE (0x0068 >> 1)
+#define SDHI_SDIO_INFO1 (0x006C >> 1)
+#define SDHI_SDIO_INFO1_MASK (0x0070 >> 1)
+#define SDHI_CC_EXT_MODE (0x01B0 >> 1)
+#define SDHI_SOFT_RST (0x01C0 >> 1)
+#define SDHI_VERSION (0x01C4 >> 1)
+#define SDHI_HOST_MODE (0x01C8 >> 1)
+#define SDHI_SDIF_MODE (0x01CC >> 1)
+#define SDHI_EXT_SWAP (0x01E0 >> 1)
+#define SDHI_SD_DMACR (0x0324 >> 1)
+
+/* SDHI CMD VALUE */
+#define CMD_MASK 0x0000ffff
+#define SDHI_APP 0x0040
+#define SDHI_SD_APP_SEND_SCR 0x0073
+#define SDHI_SD_SWITCH 0x1C06
+
+/* SDHI_PORTSEL */
+#define USE_1PORT (1 << 8) /* 1 port */
+
+/* SDHI_ARG */
+#define ARG0_MASK 0x0000ffff
+#define ARG1_MASK 0x0000ffff
+
+/* SDHI_STOP */
+#define STOP_SEC_ENABLE (1 << 8)
+
+/* SDHI_INFO1 */
+#define INFO1_RESP_END (1 << 0)
+#define INFO1_ACCESS_END (1 << 2)
+#define INFO1_CARD_RE (1 << 3)
+#define INFO1_CARD_IN (1 << 4)
+#define INFO1_ISD0CD (1 << 5)
+#define INFO1_WRITE_PRO (1 << 7)
+#define INFO1_DATA3_CARD_RE (1 << 8)
+#define INFO1_DATA3_CARD_IN (1 << 9)
+#define INFO1_DATA3 (1 << 10)
+
+/* SDHI_INFO2 */
+#define INFO2_CMD_ERROR (1 << 0)
+#define INFO2_CRC_ERROR (1 << 1)
+#define INFO2_END_ERROR (1 << 2)
+#define INFO2_TIMEOUT (1 << 3)
+#define INFO2_BUF_ILL_WRITE (1 << 4)
+#define INFO2_BUF_ILL_READ (1 << 5)
+#define INFO2_RESP_TIMEOUT (1 << 6)
+#define INFO2_SDDAT0 (1 << 7)
+#define INFO2_BRE_ENABLE (1 << 8)
+#define INFO2_BWE_ENABLE (1 << 9)
+#define INFO2_CBUSY (1 << 14)
+#define INFO2_ILA (1 << 15)
+#define INFO2_ALL_ERR (0x807f)
+
+/* SDHI_INFO1_MASK */
+#define INFO1M_RESP_END (1 << 0)
+#define INFO1M_ACCESS_END (1 << 2)
+#define INFO1M_CARD_RE (1 << 3)
+#define INFO1M_CARD_IN (1 << 4)
+#define INFO1M_DATA3_CARD_RE (1 << 8)
+#define INFO1M_DATA3_CARD_IN (1 << 9)
+#define INFO1M_ALL (0xffff)
+#define INFO1M_SET (INFO1M_RESP_END | \
+ INFO1M_ACCESS_END | \
+ INFO1M_DATA3_CARD_RE | \
+ INFO1M_DATA3_CARD_IN)
+
+/* SDHI_INFO2_MASK */
+#define INFO2M_CMD_ERROR (1 << 0)
+#define INFO2M_CRC_ERROR (1 << 1)
+#define INFO2M_END_ERROR (1 << 2)
+#define INFO2M_TIMEOUT (1 << 3)
+#define INFO2M_BUF_ILL_WRITE (1 << 4)
+#define INFO2M_BUF_ILL_READ (1 << 5)
+#define INFO2M_RESP_TIMEOUT (1 << 6)
+#define INFO2M_BRE_ENABLE (1 << 8)
+#define INFO2M_BWE_ENABLE (1 << 9)
+#define INFO2M_ILA (1 << 15)
+#define INFO2M_ALL (0xffff)
+#define INFO2M_ALL_ERR (0x807f)
+
+/* SDHI_CLK_CTRL */
+#define CLK_ENABLE (1 << 8)
+
+/* SDHI_OPTION */
+#define OPT_BUS_WIDTH_1 (1 << 15) /* bus width = 1 bit */
+
+/* SDHI_ERR_STS1 */
+#define ERR_STS1_CRC_ERROR ((1 << 11) | (1 << 10) | (1 << 9) | \
+ (1 << 8) | (1 << 5))
+#define ERR_STS1_CMD_ERROR ((1 << 4) | (1 << 3) | (1 << 2) | \
+ (1 << 1) | (1 << 0))
+
+/* SDHI_ERR_STS2 */
+#define ERR_STS2_RES_TIMEOUT (1 << 0)
+#define ERR_STS2_RES_STOP_TIMEOUT ((1 << 0) | (1 << 1))
+#define ERR_STS2_SYS_ERROR ((1 << 6) | (1 << 5) | (1 << 4) | \
+ (1 << 3) | (1 << 2) | (1 << 1) | \
+ (1 << 0))
+
+/* SDHI_SDIO_MODE */
+#define SDIO_MODE_ON (1 << 0)
+#define SDIO_MODE_OFF (0 << 0)
+
+/* SDHI_SDIO_INFO1 */
+#define SDIO_INFO1_IOIRQ (1 << 0)
+#define SDIO_INFO1_EXPUB52 (1 << 14)
+#define SDIO_INFO1_EXWT (1 << 15)
+
+/* SDHI_SDIO_INFO1_MASK */
+#define SDIO_INFO1M_CLEAR ((1 << 1) | (1 << 2))
+#define SDIO_INFO1M_ON ((1 << 15) | (1 << 14) | (1 << 2) | \
+ (1 << 1) | (1 << 0))
+
+/* SDHI_EXT_SWAP */
+#define SET_SWAP ((1 << 6) | (1 << 7)) /* SWAP */
+
+/* SDHI_SOFT_RST */
+#define SOFT_RST_ON (0 << 0)
+#define SOFT_RST_OFF (1 << 0)
+
+#define CLKDEV_SD_DATA 25000000 /* 25 MHz */
+#define CLKDEV_HS_DATA 50000000 /* 50 MHz */
+#define CLKDEV_MMC_DATA 20000000 /* 20MHz */
+#define CLKDEV_INIT 400000 /* 100 - 400 KHz */
+
+/* For quirk */
+#define SH_SDHI_QUIRK_16BIT_BUF (1)
+int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks);
+
+#endif /* _SH_SDHI_H */
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
index 96478e4..4e28c9d 100644
--- a/common/cmd_mmc.c
+++ b/common/cmd_mmc.c
@@ -73,6 +73,8 @@ U_BOOT_CMD(
static void print_mmcinfo(struct mmc *mmc)
{
+ int i;
+
printf("Device: %s\n", mmc->cfg->name);
printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24);
printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff);
@@ -92,6 +94,48 @@ static void print_mmcinfo(struct mmc *mmc)
printf("Bus Width: %d-bit%s\n", mmc->bus_width,
mmc->ddr_mode ? " DDR" : "");
+
+ puts("Erase Group Size: ");
+ print_size(((u64)mmc->erase_grp_size) << 9, "\n");
+
+ if (!IS_SD(mmc) && mmc->version >= MMC_VERSION_4_41) {
+ bool has_enh = (mmc->part_support & ENHNCD_SUPPORT) != 0;
+ bool usr_enh = has_enh && (mmc->part_attr & EXT_CSD_ENH_USR);
+
+ puts("HC WP Group Size: ");
+ print_size(((u64)mmc->hc_wp_grp_size) << 9, "\n");
+
+ puts("User Capacity: ");
+ print_size(mmc->capacity_user, usr_enh ? " ENH" : "");
+ if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_USR)
+ puts(" WRREL\n");
+ else
+ putc('\n');
+ if (usr_enh) {
+ puts("User Enhanced Start: ");
+ print_size(mmc->enh_user_start, "\n");
+ puts("User Enhanced Size: ");
+ print_size(mmc->enh_user_size, "\n");
+ }
+ puts("Boot Capacity: ");
+ print_size(mmc->capacity_boot, has_enh ? " ENH\n" : "\n");
+ puts("RPMB Capacity: ");
+ print_size(mmc->capacity_rpmb, has_enh ? " ENH\n" : "\n");
+
+ for (i = 0; i < ARRAY_SIZE(mmc->capacity_gp); i++) {
+ bool is_enh = has_enh &&
+ (mmc->part_attr & EXT_CSD_ENH_GP(i));
+ if (mmc->capacity_gp[i]) {
+ printf("GP%i Capacity: ", i+1);
+ print_size(mmc->capacity_gp[i],
+ is_enh ? " ENH" : "");
+ if (mmc->wr_rel_set & EXT_CSD_WR_DATA_REL_GP(i))
+ puts(" WRREL\n");
+ else
+ putc('\n');
+ }
+ }
+ }
}
static struct mmc *init_mmc_device(int dev, bool force_init)
{
@@ -444,6 +488,157 @@ static int do_mmc_list(cmd_tbl_t *cmdtp, int flag,
print_mmc_devices('\n');
return CMD_RET_SUCCESS;
}
+
+static int parse_hwpart_user(struct mmc_hwpart_conf *pconf,
+ int argc, char * const argv[])
+{
+ int i = 0;
+
+ memset(&pconf->user, 0, sizeof(pconf->user));
+
+ while (i < argc) {
+ if (!strcmp(argv[i], "enh")) {
+ if (i + 2 >= argc)
+ return -1;
+ pconf->user.enh_start =
+ simple_strtoul(argv[i+1], NULL, 10);
+ pconf->user.enh_size =
+ simple_strtoul(argv[i+2], NULL, 10);
+ i += 3;
+ } else if (!strcmp(argv[i], "wrrel")) {
+ if (i + 1 >= argc)
+ return -1;
+ pconf->user.wr_rel_change = 1;
+ if (!strcmp(argv[i+1], "on"))
+ pconf->user.wr_rel_set = 1;
+ else if (!strcmp(argv[i+1], "off"))
+ pconf->user.wr_rel_set = 0;
+ else
+ return -1;
+ i += 2;
+ } else {
+ break;
+ }
+ }
+ return i;
+}
+
+static int parse_hwpart_gp(struct mmc_hwpart_conf *pconf, int pidx,
+ int argc, char * const argv[])
+{
+ int i;
+
+ memset(&pconf->gp_part[pidx], 0, sizeof(pconf->gp_part[pidx]));
+
+ if (1 >= argc)
+ return -1;
+ pconf->gp_part[pidx].size = simple_strtoul(argv[0], NULL, 10);
+
+ i = 1;
+ while (i < argc) {
+ if (!strcmp(argv[i], "enh")) {
+ pconf->gp_part[pidx].enhanced = 1;
+ i += 1;
+ } else if (!strcmp(argv[i], "wrrel")) {
+ if (i + 1 >= argc)
+ return -1;
+ pconf->gp_part[pidx].wr_rel_change = 1;
+ if (!strcmp(argv[i+1], "on"))
+ pconf->gp_part[pidx].wr_rel_set = 1;
+ else if (!strcmp(argv[i+1], "off"))
+ pconf->gp_part[pidx].wr_rel_set = 0;
+ else
+ return -1;
+ i += 2;
+ } else {
+ break;
+ }
+ }
+ return i;
+}
+
+static int do_mmc_hwpartition(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct mmc *mmc;
+ struct mmc_hwpart_conf pconf = { };
+ enum mmc_hwpart_conf_mode mode = MMC_HWPART_CONF_CHECK;
+ int i, r, pidx;
+
+ mmc = init_mmc_device(curr_device, false);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (argc < 1)
+ return CMD_RET_USAGE;
+ i = 1;
+ while (i < argc) {
+ if (!strcmp(argv[i], "user")) {
+ i++;
+ r = parse_hwpart_user(&pconf, argc-i, &argv[i]);
+ if (r < 0)
+ return CMD_RET_USAGE;
+ i += r;
+ } else if (!strncmp(argv[i], "gp", 2) &&
+ strlen(argv[i]) == 3 &&
+ argv[i][2] >= '1' && argv[i][2] <= '4') {
+ pidx = argv[i][2] - '1';
+ i++;
+ r = parse_hwpart_gp(&pconf, pidx, argc-i, &argv[i]);
+ if (r < 0)
+ return CMD_RET_USAGE;
+ i += r;
+ } else if (!strcmp(argv[i], "check")) {
+ mode = MMC_HWPART_CONF_CHECK;
+ i++;
+ } else if (!strcmp(argv[i], "set")) {
+ mode = MMC_HWPART_CONF_SET;
+ i++;
+ } else if (!strcmp(argv[i], "complete")) {
+ mode = MMC_HWPART_CONF_COMPLETE;
+ i++;
+ } else {
+ return CMD_RET_USAGE;
+ }
+ }
+
+ puts("Partition configuration:\n");
+ if (pconf.user.enh_size) {
+ puts("\tUser Enhanced Start: ");
+ print_size(((u64)pconf.user.enh_start) << 9, "\n");
+ puts("\tUser Enhanced Size: ");
+ print_size(((u64)pconf.user.enh_size) << 9, "\n");
+ } else {
+ puts("\tNo enhanced user data area\n");
+ }
+ if (pconf.user.wr_rel_change)
+ printf("\tUser partition write reliability: %s\n",
+ pconf.user.wr_rel_set ? "on" : "off");
+ for (pidx = 0; pidx < 4; pidx++) {
+ if (pconf.gp_part[pidx].size) {
+ printf("\tGP%i Capacity: ", pidx+1);
+ print_size(((u64)pconf.gp_part[pidx].size) << 9,
+ pconf.gp_part[pidx].enhanced ?
+ " ENH\n" : "\n");
+ } else {
+ printf("\tNo GP%i partition\n", pidx+1);
+ }
+ if (pconf.gp_part[pidx].wr_rel_change)
+ printf("\tGP%i write reliability: %s\n", pidx+1,
+ pconf.gp_part[pidx].wr_rel_set ? "on" : "off");
+ }
+
+ if (!mmc_hwpart_config(mmc, &pconf, mode)) {
+ if (mode == MMC_HWPART_CONF_COMPLETE)
+ puts("Partitioning successful, "
+ "power-cycle to make effective\n");
+ return CMD_RET_SUCCESS;
+ } else {
+ puts("Failed!\n");
+ return CMD_RET_FAILURE;
+ }
+}
+
#ifdef CONFIG_SUPPORT_EMMC_BOOT
static int do_mmc_bootbus(cmd_tbl_t *cmdtp, int flag,
int argc, char * const argv[])
@@ -601,6 +796,7 @@ static cmd_tbl_t cmd_mmc[] = {
U_BOOT_CMD_MKENT(part, 1, 1, do_mmc_part, "", ""),
U_BOOT_CMD_MKENT(dev, 3, 0, do_mmc_dev, "", ""),
U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""),
+ U_BOOT_CMD_MKENT(hwpartition, 28, 0, do_mmc_hwpartition, "", ""),
#ifdef CONFIG_SUPPORT_EMMC_BOOT
U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""),
U_BOOT_CMD_MKENT(bootpart-resize, 4, 0, do_mmc_boot_resize, "", ""),
@@ -640,7 +836,7 @@ static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
U_BOOT_CMD(
- mmc, 7, 1, do_mmcops,
+ mmc, 29, 1, do_mmcops,
"MMC sub system",
"info - display info of the current MMC device\n"
"mmc read addr blk# cnt\n"
@@ -650,6 +846,13 @@ U_BOOT_CMD(
"mmc part - lists available partition on current mmc device\n"
"mmc dev [dev] [part] - show or set current mmc device [partition]\n"
"mmc list - lists available devices\n"
+ "mmc hwpartition [args...] - does hardware partitioning\n"
+ " arguments (sizes in 512-byte blocks):\n"
+ " [user [enh start cnt] [wrrel {on|off}]] - sets user data area attributes\n"
+ " [gp1|gp2|gp3|gp4 cnt [enh] [wrrel {on|off}]] - general purpose partition\n"
+ " [check|set|complete] - mode, complete set partitioning completed\n"
+ " WARNING: Partitioning is a write-once setting once it is set to complete.\n"
+ " Power cycling is required to initialize partitions after set to complete.\n"
#ifdef CONFIG_SUPPORT_EMMC_BOOT
"mmc bootbus dev boot_bus_width reset_boot_bus_width boot_mode\n"
" - Set the BOOT_BUS_WIDTH field of the specified device\n"
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index e69de29..7ba85a2 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -0,0 +1,9 @@
+menu "MMC Host controller Support"
+
+config SH_SDHI
+ bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support"
+ depends on RMOBILE
+ help
+ Support for the on-chip SDHI host controller on SuperH/Renesas ARM SoCs platform
+
+endmenu
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 461d7d8..4ba5878 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_S3C_SDI) += s3c_sdi.o
obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
obj-$(CONFIG_SDHCI) += sdhci.o
obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o
+obj-$(CONFIG_SH_SDHI) += sh_sdhi.o
obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o
obj-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o
obj-$(CONFIG_TEGRA_MMC) += tegra_mmc.o
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 1eb9c27..b8039cd 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -486,7 +486,7 @@ static int mmc_change_freq(struct mmc *mmc)
char cardtype;
int err;
- mmc->card_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
+ mmc->card_caps = 0;
if (mmc_host_is_spi(mmc))
return 0;
@@ -495,6 +495,8 @@ static int mmc_change_freq(struct mmc *mmc)
if (mmc->version < MMC_VERSION_4)
return 0;
+ mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
+
err = mmc_send_ext_csd(mmc, ext_csd);
if (err)
@@ -605,6 +607,200 @@ int mmc_switch_part(int dev_num, unsigned int part_num)
return ret;
}
+int mmc_hwpart_config(struct mmc *mmc,
+ const struct mmc_hwpart_conf *conf,
+ enum mmc_hwpart_conf_mode mode)
+{
+ u8 part_attrs = 0;
+ u32 enh_size_mult;
+ u32 enh_start_addr;
+ u32 gp_size_mult[4];
+ u32 max_enh_size_mult;
+ u32 tot_enh_size_mult = 0;
+ u8 wr_rel_set;
+ int i, pidx, err;
+ ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
+
+ if (mode < MMC_HWPART_CONF_CHECK || mode > MMC_HWPART_CONF_COMPLETE)
+ return -EINVAL;
+
+ if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4_41)) {
+ printf("eMMC >= 4.4 required for enhanced user data area\n");
+ return -EMEDIUMTYPE;
+ }
+
+ if (!(mmc->part_support & PART_SUPPORT)) {
+ printf("Card does not support partitioning\n");
+ return -EMEDIUMTYPE;
+ }
+
+ if (!mmc->hc_wp_grp_size) {
+ printf("Card does not define HC WP group size\n");
+ return -EMEDIUMTYPE;
+ }
+
+ /* check partition alignment and total enhanced size */
+ if (conf->user.enh_size) {
+ if (conf->user.enh_size % mmc->hc_wp_grp_size ||
+ conf->user.enh_start % mmc->hc_wp_grp_size) {
+ printf("User data enhanced area not HC WP group "
+ "size aligned\n");
+ return -EINVAL;
+ }
+ part_attrs |= EXT_CSD_ENH_USR;
+ enh_size_mult = conf->user.enh_size / mmc->hc_wp_grp_size;
+ if (mmc->high_capacity) {
+ enh_start_addr = conf->user.enh_start;
+ } else {
+ enh_start_addr = (conf->user.enh_start << 9);
+ }
+ } else {
+ enh_size_mult = 0;
+ enh_start_addr = 0;
+ }
+ tot_enh_size_mult += enh_size_mult;
+
+ for (pidx = 0; pidx < 4; pidx++) {
+ if (conf->gp_part[pidx].size % mmc->hc_wp_grp_size) {
+ printf("GP%i partition not HC WP group size "
+ "aligned\n", pidx+1);
+ return -EINVAL;
+ }
+ gp_size_mult[pidx] = conf->gp_part[pidx].size / mmc->hc_wp_grp_size;
+ if (conf->gp_part[pidx].size && conf->gp_part[pidx].enhanced) {
+ part_attrs |= EXT_CSD_ENH_GP(pidx);
+ tot_enh_size_mult += gp_size_mult[pidx];
+ }
+ }
+
+ if (part_attrs && ! (mmc->part_support & ENHNCD_SUPPORT)) {
+ printf("Card does not support enhanced attribute\n");
+ return -EMEDIUMTYPE;
+ }
+
+ err = mmc_send_ext_csd(mmc, ext_csd);
+ if (err)
+ return err;
+
+ max_enh_size_mult =
+ (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+2] << 16) +
+ (ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT+1] << 8) +
+ ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT];
+ if (tot_enh_size_mult > max_enh_size_mult) {
+ printf("Total enhanced size exceeds maximum (%u > %u)\n",
+ tot_enh_size_mult, max_enh_size_mult);
+ return -EMEDIUMTYPE;
+ }
+
+ /* The default value of EXT_CSD_WR_REL_SET is device
+ * dependent, the values can only be changed if the
+ * EXT_CSD_HS_CTRL_REL bit is set. The values can be
+ * changed only once and before partitioning is completed. */
+ wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
+ if (conf->user.wr_rel_change) {
+ if (conf->user.wr_rel_set)
+ wr_rel_set |= EXT_CSD_WR_DATA_REL_USR;
+ else
+ wr_rel_set &= ~EXT_CSD_WR_DATA_REL_USR;
+ }
+ for (pidx = 0; pidx < 4; pidx++) {
+ if (conf->gp_part[pidx].wr_rel_change) {
+ if (conf->gp_part[pidx].wr_rel_set)
+ wr_rel_set |= EXT_CSD_WR_DATA_REL_GP(pidx);
+ else
+ wr_rel_set &= ~EXT_CSD_WR_DATA_REL_GP(pidx);
+ }
+ }
+
+ if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET] &&
+ !(ext_csd[EXT_CSD_WR_REL_PARAM] & EXT_CSD_HS_CTRL_REL)) {
+ puts("Card does not support host controlled partition write "
+ "reliability settings\n");
+ return -EMEDIUMTYPE;
+ }
+
+ if (ext_csd[EXT_CSD_PARTITION_SETTING] &
+ EXT_CSD_PARTITION_SETTING_COMPLETED) {
+ printf("Card already partitioned\n");
+ return -EPERM;
+ }
+
+ if (mode == MMC_HWPART_CONF_CHECK)
+ return 0;
+
+ /* Partitioning requires high-capacity size definitions */
+ if (!(ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01)) {
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_ERASE_GROUP_DEF, 1);
+
+ if (err)
+ return err;
+
+ ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
+
+ /* update erase group size to be high-capacity */
+ mmc->erase_grp_size =
+ ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
+
+ }
+
+ /* all OK, write the configuration */
+ for (i = 0; i < 4; i++) {
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_ENH_START_ADDR+i,
+ (enh_start_addr >> (i*8)) & 0xFF);
+ if (err)
+ return err;
+ }
+ for (i = 0; i < 3; i++) {
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_ENH_SIZE_MULT+i,
+ (enh_size_mult >> (i*8)) & 0xFF);
+ if (err)
+ return err;
+ }
+ for (pidx = 0; pidx < 4; pidx++) {
+ for (i = 0; i < 3; i++) {
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_GP_SIZE_MULT+pidx*3+i,
+ (gp_size_mult[pidx] >> (i*8)) & 0xFF);
+ if (err)
+ return err;
+ }
+ }
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_PARTITIONS_ATTRIBUTE, part_attrs);
+ if (err)
+ return err;
+
+ if (mode == MMC_HWPART_CONF_SET)
+ return 0;
+
+ /* The WR_REL_SET is a write-once register but shall be
+ * written before setting PART_SETTING_COMPLETED. As it is
+ * write-once we can only write it when completing the
+ * partitioning. */
+ if (wr_rel_set != ext_csd[EXT_CSD_WR_REL_SET]) {
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_WR_REL_SET, wr_rel_set);
+ if (err)
+ return err;
+ }
+
+ /* Setting PART_SETTING_COMPLETED confirms the partition
+ * configuration but it only becomes effective after power
+ * cycle, so we do not adjust the partition related settings
+ * in the mmc struct. */
+
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_PARTITION_SETTING,
+ EXT_CSD_PARTITION_SETTING_COMPLETED);
+ if (err)
+ return err;
+
+ return 0;
+}
+
int mmc_getcd(struct mmc *mmc)
{
int cd;
@@ -818,6 +1014,8 @@ static int mmc_startup(struct mmc *mmc)
ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN);
int timeout = 1000;
+ bool has_parts = false;
+ bool part_completed;
#ifdef CONFIG_MMC_SPI_CRC_ON
if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
@@ -970,7 +1168,9 @@ static int mmc_startup(struct mmc *mmc)
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[EXT_CSD_REV] >= 2)) {
+ if (err)
+ return err;
+ if (ext_csd[EXT_CSD_REV] >= 2) {
/*
* According to the JEDEC Standard, the value of
* ext_csd's capacity is valid if the value is more
@@ -1006,13 +1206,70 @@ static int mmc_startup(struct mmc *mmc)
break;
}
+ /* The partition data may be non-zero but it is only
+ * effective if PARTITION_SETTING_COMPLETED is set in
+ * EXT_CSD, so ignore any data if this bit is not set,
+ * except for enabling the high-capacity group size
+ * definition (see below). */
+ part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] &
+ EXT_CSD_PARTITION_SETTING_COMPLETED);
+
+ /* store the partition info of emmc */
+ mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT];
+ if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
+ ext_csd[EXT_CSD_BOOT_MULT])
+ mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
+ if (part_completed &&
+ (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & ENHNCD_SUPPORT))
+ mmc->part_attr = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE];
+
+ mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
+
+ mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
+
+ for (i = 0; i < 4; i++) {
+ int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
+ uint mult = (ext_csd[idx + 2] << 16) +
+ (ext_csd[idx + 1] << 8) + ext_csd[idx];
+ if (mult)
+ has_parts = true;
+ if (!part_completed)
+ continue;
+ mmc->capacity_gp[i] = mult;
+ mmc->capacity_gp[i] *=
+ ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
+ mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+ mmc->capacity_gp[i] <<= 19;
+ }
+
+ if (part_completed) {
+ mmc->enh_user_size =
+ (ext_csd[EXT_CSD_ENH_SIZE_MULT+2] << 16) +
+ (ext_csd[EXT_CSD_ENH_SIZE_MULT+1] << 8) +
+ ext_csd[EXT_CSD_ENH_SIZE_MULT];
+ mmc->enh_user_size *= ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
+ mmc->enh_user_size *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+ mmc->enh_user_size <<= 19;
+ mmc->enh_user_start =
+ (ext_csd[EXT_CSD_ENH_START_ADDR+3] << 24) +
+ (ext_csd[EXT_CSD_ENH_START_ADDR+2] << 16) +
+ (ext_csd[EXT_CSD_ENH_START_ADDR+1] << 8) +
+ ext_csd[EXT_CSD_ENH_START_ADDR];
+ if (mmc->high_capacity)
+ mmc->enh_user_start <<= 9;
+ }
+
/*
* Host needs to enable ERASE_GRP_DEF bit if device is
* partitioned. This bit will be lost every time after a reset
* or power off. This will affect erase size.
*/
+ if (part_completed)
+ has_parts = true;
if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) &&
- (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB)) {
+ (ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] & PART_ENH_ATTRIB))
+ has_parts = true;
+ if (has_parts) {
err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_ERASE_GROUP_DEF, 1);
@@ -1020,19 +1277,18 @@ static int mmc_startup(struct mmc *mmc)
return err;
else
ext_csd[EXT_CSD_ERASE_GROUP_DEF] = 1;
+ }
+ if (ext_csd[EXT_CSD_ERASE_GROUP_DEF] & 0x01) {
/* Read out group size from ext_csd */
mmc->erase_grp_size =
- ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] *
- MMC_MAX_BLOCK_LEN * 1024;
+ ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 1024;
/*
* if high capacity and partition setting completed
* SEC_COUNT is valid even if it is smaller than 2 GiB
* JEDEC Standard JESD84-B45, 6.2.4
*/
- if (mmc->high_capacity &&
- (ext_csd[EXT_CSD_PARTITION_SETTING] &
- EXT_CSD_PARTITION_SETTING_COMPLETED)) {
+ if (mmc->high_capacity && part_completed) {
capacity = (ext_csd[EXT_CSD_SEC_CNT]) |
(ext_csd[EXT_CSD_SEC_CNT + 1] << 8) |
(ext_csd[EXT_CSD_SEC_CNT + 2] << 16) |
@@ -1049,23 +1305,11 @@ static int mmc_startup(struct mmc *mmc)
* (erase_gmul + 1);
}
- /* store the partition info of emmc */
- if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) ||
- ext_csd[EXT_CSD_BOOT_MULT])
- mmc->part_config = ext_csd[EXT_CSD_PART_CONF];
-
- mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17;
-
- mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17;
+ mmc->hc_wp_grp_size = 1024
+ * ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]
+ * ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
- for (i = 0; i < 4; i++) {
- int idx = EXT_CSD_GP_SIZE_MULT + i * 3;
- mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) +
- (ext_csd[idx + 1] << 8) + ext_csd[idx];
- mmc->capacity_gp[i] *=
- ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
- mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
- }
+ mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
}
err = mmc_set_capacity(mmc, mmc->part_num);
@@ -1107,7 +1351,8 @@ static int mmc_startup(struct mmc *mmc)
mmc->tran_speed = 50000000;
else
mmc->tran_speed = 25000000;
- } else {
+ } else if (mmc->version >= MMC_VERSION_4) {
+ /* Only version 4 of MMC supports wider bus widths */
int idx;
/* An array of possible bus widths in order of preference */
@@ -1139,6 +1384,18 @@ static int mmc_startup(struct mmc *mmc)
unsigned int caps = ext_to_hostcaps[extw];
/*
+ * If the bus width is still not changed,
+ * don't try to set the default again.
+ * Otherwise, recover from switch attempts
+ * by switching to 1-bit bus width.
+ */
+ if (extw == EXT_CSD_BUS_WIDTH_1 &&
+ mmc->bus_width == 1) {
+ err = 0;
+ break;
+ }
+
+ /*
* Check to make sure the card and controller support
* these capabilities
*/
diff --git a/drivers/mmc/sh_sdhi.c b/drivers/mmc/sh_sdhi.c
new file mode 100644
index 0000000..cc62c89
--- /dev/null
+++ b/drivers/mmc/sh_sdhi.c
@@ -0,0 +1,695 @@
+/*
+ * drivers/mmc/sh_sdhi.c
+ *
+ * SD/MMC driver for Renesas rmobile ARM SoCs.
+ *
+ * Copyright (C) 2011,2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2014 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
+ * Copyright (C) 2008-2009 Renesas Solutions Corp.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/arch/rmobile.h>
+#include <asm/arch/sh_sdhi.h>
+
+#define DRIVER_NAME "sh-sdhi"
+
+struct sh_sdhi_host {
+ unsigned long addr;
+ int ch;
+ int bus_shift;
+ unsigned long quirks;
+ unsigned char wait_int;
+ unsigned char sd_error;
+ unsigned char detect_waiting;
+};
+static inline void sh_sdhi_writew(struct sh_sdhi_host *host, int reg, u16 val)
+{
+ writew(val, host->addr + (reg << host->bus_shift));
+}
+
+static inline u16 sh_sdhi_readw(struct sh_sdhi_host *host, int reg)
+{
+ return readw(host->addr + (reg << host->bus_shift));
+}
+
+static void *mmc_priv(struct mmc *mmc)
+{
+ return (void *)mmc->priv;
+}
+
+static void sh_sdhi_detect(struct sh_sdhi_host *host)
+{
+ sh_sdhi_writew(host, SDHI_OPTION,
+ OPT_BUS_WIDTH_1 | sh_sdhi_readw(host, SDHI_OPTION));
+
+ host->detect_waiting = 0;
+}
+
+static int sh_sdhi_intr(void *dev_id)
+{
+ struct sh_sdhi_host *host = dev_id;
+ int state1 = 0, state2 = 0;
+
+ state1 = sh_sdhi_readw(host, SDHI_INFO1);
+ state2 = sh_sdhi_readw(host, SDHI_INFO2);
+
+ debug("%s: state1 = %x, state2 = %x\n", __func__, state1, state2);
+
+ /* CARD Insert */
+ if (state1 & INFO1_CARD_IN) {
+ sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_IN);
+ if (!host->detect_waiting) {
+ host->detect_waiting = 1;
+ sh_sdhi_detect(host);
+ }
+ sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
+ INFO1M_ACCESS_END | INFO1M_CARD_IN |
+ INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
+ return -EAGAIN;
+ }
+ /* CARD Removal */
+ if (state1 & INFO1_CARD_RE) {
+ sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_CARD_RE);
+ if (!host->detect_waiting) {
+ host->detect_waiting = 1;
+ sh_sdhi_detect(host);
+ }
+ sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
+ INFO1M_ACCESS_END | INFO1M_CARD_RE |
+ INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
+ sh_sdhi_writew(host, SDHI_SDIO_INFO1_MASK, SDIO_INFO1M_ON);
+ sh_sdhi_writew(host, SDHI_SDIO_MODE, SDIO_MODE_OFF);
+ return -EAGAIN;
+ }
+
+ if (state2 & INFO2_ALL_ERR) {
+ sh_sdhi_writew(host, SDHI_INFO2,
+ (unsigned short)~(INFO2_ALL_ERR));
+ sh_sdhi_writew(host, SDHI_INFO2_MASK,
+ INFO2M_ALL_ERR |
+ sh_sdhi_readw(host, SDHI_INFO2_MASK));
+ host->sd_error = 1;
+ host->wait_int = 1;
+ return 0;
+ }
+ /* Respons End */
+ if (state1 & INFO1_RESP_END) {
+ sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
+ sh_sdhi_writew(host, SDHI_INFO1_MASK,
+ INFO1M_RESP_END |
+ sh_sdhi_readw(host, SDHI_INFO1_MASK));
+ host->wait_int = 1;
+ return 0;
+ }
+ /* SD_BUF Read Enable */
+ if (state2 & INFO2_BRE_ENABLE) {
+ sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BRE_ENABLE);
+ sh_sdhi_writew(host, SDHI_INFO2_MASK,
+ INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ |
+ sh_sdhi_readw(host, SDHI_INFO2_MASK));
+ host->wait_int = 1;
+ return 0;
+ }
+ /* SD_BUF Write Enable */
+ if (state2 & INFO2_BWE_ENABLE) {
+ sh_sdhi_writew(host, SDHI_INFO2, ~INFO2_BWE_ENABLE);
+ sh_sdhi_writew(host, SDHI_INFO2_MASK,
+ INFO2_BWE_ENABLE | INFO2M_BUF_ILL_WRITE |
+ sh_sdhi_readw(host, SDHI_INFO2_MASK));
+ host->wait_int = 1;
+ return 0;
+ }
+ /* Access End */
+ if (state1 & INFO1_ACCESS_END) {
+ sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_ACCESS_END);
+ sh_sdhi_writew(host, SDHI_INFO1_MASK,
+ INFO1_ACCESS_END |
+ sh_sdhi_readw(host, SDHI_INFO1_MASK));
+ host->wait_int = 1;
+ return 0;
+ }
+ return -EAGAIN;
+}
+
+static int sh_sdhi_wait_interrupt_flag(struct sh_sdhi_host *host)
+{
+ int timeout = 10000000;
+
+ while (1) {
+ timeout--;
+ if (timeout < 0) {
+ debug(DRIVER_NAME": %s timeout\n", __func__);
+ return 0;
+ }
+
+ if (!sh_sdhi_intr(host))
+ break;
+
+ udelay(1); /* 1 usec */
+ }
+
+ return 1; /* Return value: NOT 0 = complete waiting */
+}
+
+static int sh_sdhi_clock_control(struct sh_sdhi_host *host, unsigned long clk)
+{
+ u32 clkdiv, i, timeout;
+
+ if (sh_sdhi_readw(host, SDHI_INFO2) & (1 << 14)) {
+ printf(DRIVER_NAME": Busy state ! Cannot change the clock\n");
+ return -EBUSY;
+ }
+
+ sh_sdhi_writew(host, SDHI_CLK_CTRL,
+ ~CLK_ENABLE & sh_sdhi_readw(host, SDHI_CLK_CTRL));
+
+ if (clk == 0)
+ return -EIO;
+
+ clkdiv = 0x80;
+ i = CONFIG_SH_SDHI_FREQ >> (0x8 + 1);
+ for (; clkdiv && clk >= (i << 1); (clkdiv >>= 1))
+ i <<= 1;
+
+ sh_sdhi_writew(host, SDHI_CLK_CTRL, clkdiv);
+
+ timeout = 100000;
+ /* Waiting for SD Bus busy to be cleared */
+ while (timeout--) {
+ if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
+ break;
+ }
+
+ if (timeout)
+ sh_sdhi_writew(host, SDHI_CLK_CTRL,
+ CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
+ else
+ return -EBUSY;
+
+ return 0;
+}
+
+static int sh_sdhi_sync_reset(struct sh_sdhi_host *host)
+{
+ u32 timeout;
+ sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_ON);
+ sh_sdhi_writew(host, SDHI_SOFT_RST, SOFT_RST_OFF);
+ sh_sdhi_writew(host, SDHI_CLK_CTRL,
+ CLK_ENABLE | sh_sdhi_readw(host, SDHI_CLK_CTRL));
+
+ timeout = 100000;
+ while (timeout--) {
+ if (!(sh_sdhi_readw(host, SDHI_INFO2) & INFO2_CBUSY))
+ break;
+ udelay(100);
+ }
+
+ if (!timeout)
+ return -EBUSY;
+
+ if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
+ sh_sdhi_writew(host, SDHI_HOST_MODE, 1);
+
+ return 0;
+}
+
+static int sh_sdhi_error_manage(struct sh_sdhi_host *host)
+{
+ unsigned short e_state1, e_state2;
+ int ret;
+
+ host->sd_error = 0;
+ host->wait_int = 0;
+
+ e_state1 = sh_sdhi_readw(host, SDHI_ERR_STS1);
+ e_state2 = sh_sdhi_readw(host, SDHI_ERR_STS2);
+ if (e_state2 & ERR_STS2_SYS_ERROR) {
+ if (e_state2 & ERR_STS2_RES_STOP_TIMEOUT)
+ ret = TIMEOUT;
+ else
+ ret = -EILSEQ;
+ debug("%s: ERR_STS2 = %04x\n",
+ DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS2));
+ sh_sdhi_sync_reset(host);
+
+ sh_sdhi_writew(host, SDHI_INFO1_MASK,
+ INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
+ return ret;
+ }
+ if (e_state1 & ERR_STS1_CRC_ERROR || e_state1 & ERR_STS1_CMD_ERROR)
+ ret = -EILSEQ;
+ else
+ ret = TIMEOUT;
+
+ debug("%s: ERR_STS1 = %04x\n",
+ DRIVER_NAME, sh_sdhi_readw(host, SDHI_ERR_STS1));
+ sh_sdhi_sync_reset(host);
+ sh_sdhi_writew(host, SDHI_INFO1_MASK,
+ INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
+ return ret;
+}
+
+static int sh_sdhi_single_read(struct sh_sdhi_host *host, struct mmc_data *data)
+{
+ long time;
+ unsigned short blocksize, i;
+ unsigned short *p = (unsigned short *)data->dest;
+
+ if ((unsigned long)p & 0x00000001) {
+ debug(DRIVER_NAME": %s: The data pointer is unaligned.",
+ __func__);
+ return -EIO;
+ }
+
+ host->wait_int = 0;
+ sh_sdhi_writew(host, SDHI_INFO2_MASK,
+ ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
+ sh_sdhi_readw(host, SDHI_INFO2_MASK));
+ sh_sdhi_writew(host, SDHI_INFO1_MASK,
+ ~INFO1M_ACCESS_END &
+ sh_sdhi_readw(host, SDHI_INFO1_MASK));
+ time = sh_sdhi_wait_interrupt_flag(host);
+ if (time == 0 || host->sd_error != 0)
+ return sh_sdhi_error_manage(host);
+
+ host->wait_int = 0;
+ blocksize = sh_sdhi_readw(host, SDHI_SIZE);
+ for (i = 0; i < blocksize / 2; i++)
+ *p++ = sh_sdhi_readw(host, SDHI_BUF0);
+
+ time = sh_sdhi_wait_interrupt_flag(host);
+ if (time == 0 || host->sd_error != 0)
+ return sh_sdhi_error_manage(host);
+
+ host->wait_int = 0;
+ return 0;
+}
+
+static int sh_sdhi_multi_read(struct sh_sdhi_host *host, struct mmc_data *data)
+{
+ long time;
+ unsigned short blocksize, i, sec;
+ unsigned short *p = (unsigned short *)data->dest;
+
+ if ((unsigned long)p & 0x00000001) {
+ debug(DRIVER_NAME": %s: The data pointer is unaligned.",
+ __func__);
+ return -EIO;
+ }
+
+ debug("%s: blocks = %d, blocksize = %d\n",
+ __func__, data->blocks, data->blocksize);
+
+ host->wait_int = 0;
+ for (sec = 0; sec < data->blocks; sec++) {
+ sh_sdhi_writew(host, SDHI_INFO2_MASK,
+ ~(INFO2M_BRE_ENABLE | INFO2M_BUF_ILL_READ) &
+ sh_sdhi_readw(host, SDHI_INFO2_MASK));
+
+ time = sh_sdhi_wait_interrupt_flag(host);
+ if (time == 0 || host->sd_error != 0)
+ return sh_sdhi_error_manage(host);
+
+ host->wait_int = 0;
+ blocksize = sh_sdhi_readw(host, SDHI_SIZE);
+ for (i = 0; i < blocksize / 2; i++)
+ *p++ = sh_sdhi_readw(host, SDHI_BUF0);
+ }
+
+ return 0;
+}
+
+static int sh_sdhi_single_write(struct sh_sdhi_host *host,
+ struct mmc_data *data)
+{
+ long time;
+ unsigned short blocksize, i;
+ const unsigned short *p = (const unsigned short *)data->src;
+
+ if ((unsigned long)p & 0x00000001) {
+ debug(DRIVER_NAME": %s: The data pointer is unaligned.",
+ __func__);
+ return -EIO;
+ }
+
+ debug("%s: blocks = %d, blocksize = %d\n",
+ __func__, data->blocks, data->blocksize);
+
+ host->wait_int = 0;
+ sh_sdhi_writew(host, SDHI_INFO2_MASK,
+ ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
+ sh_sdhi_readw(host, SDHI_INFO2_MASK));
+ sh_sdhi_writew(host, SDHI_INFO1_MASK,
+ ~INFO1M_ACCESS_END &
+ sh_sdhi_readw(host, SDHI_INFO1_MASK));
+
+ time = sh_sdhi_wait_interrupt_flag(host);
+ if (time == 0 || host->sd_error != 0)
+ return sh_sdhi_error_manage(host);
+
+ host->wait_int = 0;
+ blocksize = sh_sdhi_readw(host, SDHI_SIZE);
+ for (i = 0; i < blocksize / 2; i++)
+ sh_sdhi_writew(host, SDHI_BUF0, *p++);
+
+ time = sh_sdhi_wait_interrupt_flag(host);
+ if (time == 0 || host->sd_error != 0)
+ return sh_sdhi_error_manage(host);
+
+ host->wait_int = 0;
+ return 0;
+}
+
+static int sh_sdhi_multi_write(struct sh_sdhi_host *host, struct mmc_data *data)
+{
+ long time;
+ unsigned short i, sec, blocksize;
+ const unsigned short *p = (const unsigned short *)data->src;
+
+ debug("%s: blocks = %d, blocksize = %d\n",
+ __func__, data->blocks, data->blocksize);
+
+ host->wait_int = 0;
+ for (sec = 0; sec < data->blocks; sec++) {
+ sh_sdhi_writew(host, SDHI_INFO2_MASK,
+ ~(INFO2M_BWE_ENABLE | INFO2M_BUF_ILL_WRITE) &
+ sh_sdhi_readw(host, SDHI_INFO2_MASK));
+
+ time = sh_sdhi_wait_interrupt_flag(host);
+ if (time == 0 || host->sd_error != 0)
+ return sh_sdhi_error_manage(host);
+
+ host->wait_int = 0;
+ blocksize = sh_sdhi_readw(host, SDHI_SIZE);
+ for (i = 0; i < blocksize / 2; i++)
+ sh_sdhi_writew(host, SDHI_BUF0, *p++);
+ }
+
+ return 0;
+}
+
+static void sh_sdhi_get_response(struct sh_sdhi_host *host, struct mmc_cmd *cmd)
+{
+ unsigned short i, j, cnt = 1;
+ unsigned short resp[8];
+ unsigned long *p1, *p2;
+
+ if (cmd->resp_type & MMC_RSP_136) {
+ cnt = 4;
+ resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
+ resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
+ resp[2] = sh_sdhi_readw(host, SDHI_RSP02);
+ resp[3] = sh_sdhi_readw(host, SDHI_RSP03);
+ resp[4] = sh_sdhi_readw(host, SDHI_RSP04);
+ resp[5] = sh_sdhi_readw(host, SDHI_RSP05);
+ resp[6] = sh_sdhi_readw(host, SDHI_RSP06);
+ resp[7] = sh_sdhi_readw(host, SDHI_RSP07);
+
+ /* SDHI REGISTER SPECIFICATION */
+ for (i = 7, j = 6; i > 0; i--) {
+ resp[i] = (resp[i] << 8) & 0xff00;
+ resp[i] |= (resp[j--] >> 8) & 0x00ff;
+ }
+ resp[0] = (resp[0] << 8) & 0xff00;
+
+ /* SDHI REGISTER SPECIFICATION */
+ p1 = ((unsigned long *)resp) + 3;
+
+ } else {
+ resp[0] = sh_sdhi_readw(host, SDHI_RSP00);
+ resp[1] = sh_sdhi_readw(host, SDHI_RSP01);
+
+ p1 = ((unsigned long *)resp);
+ }
+
+ p2 = (unsigned long *)cmd->response;
+#if defined(__BIG_ENDIAN_BITFIELD)
+ for (i = 0; i < cnt; i++) {
+ *p2++ = ((*p1 >> 16) & 0x0000ffff) |
+ ((*p1 << 16) & 0xffff0000);
+ p1--;
+ }
+#else
+ for (i = 0; i < cnt; i++)
+ *p2++ = *p1--;
+#endif /* __BIG_ENDIAN_BITFIELD */
+}
+
+static unsigned short sh_sdhi_set_cmd(struct sh_sdhi_host *host,
+ struct mmc_data *data, unsigned short opc)
+{
+ switch (opc) {
+ case SD_CMD_APP_SEND_OP_COND:
+ case SD_CMD_APP_SEND_SCR:
+ opc |= SDHI_APP;
+ break;
+ case SD_CMD_APP_SET_BUS_WIDTH:
+ /* SD_APP_SET_BUS_WIDTH*/
+ if (!data)
+ opc |= SDHI_APP;
+ else /* SD_SWITCH */
+ opc = SDHI_SD_SWITCH;
+ break;
+ default:
+ break;
+ }
+ return opc;
+}
+
+static unsigned short sh_sdhi_data_trans(struct sh_sdhi_host *host,
+ struct mmc_data *data, unsigned short opc)
+{
+ unsigned short ret;
+
+ switch (opc) {
+ case MMC_CMD_READ_MULTIPLE_BLOCK:
+ ret = sh_sdhi_multi_read(host, data);
+ break;
+ case MMC_CMD_WRITE_MULTIPLE_BLOCK:
+ ret = sh_sdhi_multi_write(host, data);
+ break;
+ case MMC_CMD_WRITE_SINGLE_BLOCK:
+ ret = sh_sdhi_single_write(host, data);
+ break;
+ case MMC_CMD_READ_SINGLE_BLOCK:
+ case SDHI_SD_APP_SEND_SCR:
+ case SDHI_SD_SWITCH: /* SD_SWITCH */
+ ret = sh_sdhi_single_read(host, data);
+ break;
+ default:
+ printf(DRIVER_NAME": SD: NOT SUPPORT CMD = d'%04d\n", opc);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int sh_sdhi_start_cmd(struct sh_sdhi_host *host,
+ struct mmc_data *data, struct mmc_cmd *cmd)
+{
+ long time;
+ unsigned short opc = cmd->cmdidx;
+ int ret = 0;
+ unsigned long timeout;
+
+ debug("opc = %d, arg = %x, resp_type = %x\n",
+ opc, cmd->cmdarg, cmd->resp_type);
+
+ if (opc == MMC_CMD_STOP_TRANSMISSION) {
+ /* SDHI sends the STOP command automatically by STOP reg */
+ sh_sdhi_writew(host, SDHI_INFO1_MASK, ~INFO1M_ACCESS_END &
+ sh_sdhi_readw(host, SDHI_INFO1_MASK));
+
+ time = sh_sdhi_wait_interrupt_flag(host);
+ if (time == 0 || host->sd_error != 0)
+ return sh_sdhi_error_manage(host);
+
+ sh_sdhi_get_response(host, cmd);
+ return 0;
+ }
+
+ if (data) {
+ if ((opc == MMC_CMD_READ_MULTIPLE_BLOCK) ||
+ opc == MMC_CMD_WRITE_MULTIPLE_BLOCK) {
+ sh_sdhi_writew(host, SDHI_STOP, STOP_SEC_ENABLE);
+ sh_sdhi_writew(host, SDHI_SECCNT, data->blocks);
+ }
+ sh_sdhi_writew(host, SDHI_SIZE, data->blocksize);
+ }
+ opc = sh_sdhi_set_cmd(host, data, opc);
+
+ /*
+ * U-boot cannot use interrupt.
+ * So this flag may not be clear by timing
+ */
+ sh_sdhi_writew(host, SDHI_INFO1, ~INFO1_RESP_END);
+
+ sh_sdhi_writew(host, SDHI_INFO1_MASK,
+ INFO1M_RESP_END | sh_sdhi_readw(host, SDHI_INFO1_MASK));
+ sh_sdhi_writew(host, SDHI_ARG0,
+ (unsigned short)(cmd->cmdarg & ARG0_MASK));
+ sh_sdhi_writew(host, SDHI_ARG1,
+ (unsigned short)((cmd->cmdarg >> 16) & ARG1_MASK));
+
+ timeout = 100000;
+ /* Waiting for SD Bus busy to be cleared */
+ while (timeout--) {
+ if ((sh_sdhi_readw(host, SDHI_INFO2) & 0x2000))
+ break;
+ }
+
+ sh_sdhi_writew(host, SDHI_CMD, (unsigned short)(opc & CMD_MASK));
+
+ host->wait_int = 0;
+ sh_sdhi_writew(host, SDHI_INFO1_MASK,
+ ~INFO1M_RESP_END & sh_sdhi_readw(host, SDHI_INFO1_MASK));
+ sh_sdhi_writew(host, SDHI_INFO2_MASK,
+ ~(INFO2M_CMD_ERROR | INFO2M_CRC_ERROR |
+ INFO2M_END_ERROR | INFO2M_TIMEOUT |
+ INFO2M_RESP_TIMEOUT | INFO2M_ILA) &
+ sh_sdhi_readw(host, SDHI_INFO2_MASK));
+
+ time = sh_sdhi_wait_interrupt_flag(host);
+ if (!time)
+ return sh_sdhi_error_manage(host);
+
+ if (host->sd_error) {
+ switch (cmd->cmdidx) {
+ case MMC_CMD_ALL_SEND_CID:
+ case MMC_CMD_SELECT_CARD:
+ case SD_CMD_SEND_IF_COND:
+ case MMC_CMD_APP_CMD:
+ ret = TIMEOUT;
+ break;
+ default:
+ debug(DRIVER_NAME": Cmd(d'%d) err\n", opc);
+ debug(DRIVER_NAME": cmdidx = %d\n", cmd->cmdidx);
+ ret = sh_sdhi_error_manage(host);
+ break;
+ }
+ host->sd_error = 0;
+ host->wait_int = 0;
+ return ret;
+ }
+ if (sh_sdhi_readw(host, SDHI_INFO1) & INFO1_RESP_END)
+ return -EINVAL;
+
+ if (host->wait_int) {
+ sh_sdhi_get_response(host, cmd);
+ host->wait_int = 0;
+ }
+ if (data)
+ ret = sh_sdhi_data_trans(host, data, opc);
+
+ debug("ret = %d, resp = %08x, %08x, %08x, %08x\n",
+ ret, cmd->response[0], cmd->response[1],
+ cmd->response[2], cmd->response[3]);
+ return ret;
+}
+
+static int sh_sdhi_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ struct sh_sdhi_host *host = mmc_priv(mmc);
+ int ret;
+
+ host->sd_error = 0;
+
+ ret = sh_sdhi_start_cmd(host, data, cmd);
+
+ return ret;
+}
+
+static void sh_sdhi_set_ios(struct mmc *mmc)
+{
+ int ret;
+ struct sh_sdhi_host *host = mmc_priv(mmc);
+
+ ret = sh_sdhi_clock_control(host, mmc->clock);
+ if (ret)
+ return;
+
+ if (mmc->bus_width == 4)
+ sh_sdhi_writew(host, SDHI_OPTION, ~OPT_BUS_WIDTH_1 &
+ sh_sdhi_readw(host, SDHI_OPTION));
+ else
+ sh_sdhi_writew(host, SDHI_OPTION, OPT_BUS_WIDTH_1 |
+ sh_sdhi_readw(host, SDHI_OPTION));
+
+ debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width);
+}
+
+static int sh_sdhi_initialize(struct mmc *mmc)
+{
+ struct sh_sdhi_host *host = mmc_priv(mmc);
+ int ret = sh_sdhi_sync_reset(host);
+
+ sh_sdhi_writew(host, SDHI_PORTSEL, USE_1PORT);
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+ sh_sdhi_writew(host, SDHI_EXT_SWAP, SET_SWAP);
+#endif
+
+ sh_sdhi_writew(host, SDHI_INFO1_MASK, INFO1M_RESP_END |
+ INFO1M_ACCESS_END | INFO1M_CARD_RE |
+ INFO1M_DATA3_CARD_RE | INFO1M_DATA3_CARD_IN);
+
+ return ret;
+}
+
+static const struct mmc_ops sh_sdhi_ops = {
+ .send_cmd = sh_sdhi_send_cmd,
+ .set_ios = sh_sdhi_set_ios,
+ .init = sh_sdhi_initialize,
+};
+
+static struct mmc_config sh_sdhi_cfg = {
+ .name = DRIVER_NAME,
+ .ops = &sh_sdhi_ops,
+ .f_min = CLKDEV_INIT,
+ .f_max = CLKDEV_HS_DATA,
+ .voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
+ .host_caps = MMC_MODE_4BIT | MMC_MODE_HS,
+ .part_type = PART_TYPE_DOS,
+ .b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
+};
+
+int sh_sdhi_init(unsigned long addr, int ch, unsigned long quirks)
+{
+ int ret = 0;
+ struct mmc *mmc;
+ struct sh_sdhi_host *host = NULL;
+
+ if (ch >= CONFIG_SYS_SH_SDHI_NR_CHANNEL)
+ return -ENODEV;
+
+ host = malloc(sizeof(struct sh_sdhi_host));
+ if (!host)
+ return -ENOMEM;
+
+ mmc = mmc_create(&sh_sdhi_cfg, host);
+ if (!mmc) {
+ ret = -1;
+ goto error;
+ }
+
+ host->ch = ch;
+ host->addr = addr;
+ host->quirks = quirks;
+
+ if (host->quirks & SH_SDHI_QUIRK_16BIT_BUF)
+ host->bus_shift = 1;
+
+ return ret;
+error:
+ if (host)
+ free(host);
+ return ret;
+}
diff --git a/include/mmc.h b/include/mmc.h
index 7ec255d..09101e2 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -147,11 +147,16 @@
/*
* EXT_CSD fields
*/
+#define EXT_CSD_ENH_START_ADDR 136 /* R/W */
+#define EXT_CSD_ENH_SIZE_MULT 140 /* R/W */
#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */
#define EXT_CSD_PARTITION_SETTING 155 /* R/W */
#define EXT_CSD_PARTITIONS_ATTRIBUTE 156 /* R/W */
+#define EXT_CSD_MAX_ENH_SIZE_MULT 157 /* R */
#define EXT_CSD_PARTITIONING_SUPPORT 160 /* RO */
#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */
+#define EXT_CSD_WR_REL_PARAM 166 /* R */
+#define EXT_CSD_WR_REL_SET 167 /* R/W */
#define EXT_CSD_RPMB_MULT 168 /* RO */
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
#define EXT_CSD_BOOT_BUS_WIDTH 177
@@ -201,6 +206,14 @@
#define EXT_CSD_PARTITION_SETTING_COMPLETED (1 << 0)
+#define EXT_CSD_ENH_USR (1 << 0) /* user data area is enhanced */
+#define EXT_CSD_ENH_GP(x) (1 << ((x)+1)) /* GP part (x+1) is enhanced */
+
+#define EXT_CSD_HS_CTRL_REL (1 << 0) /* host controlled WR_REL_SET */
+
+#define EXT_CSD_WR_DATA_REL_USR (1 << 0) /* user data area WR_REL */
+#define EXT_CSD_WR_DATA_REL_GP(x) (1 << ((x)+1)) /* GP part (x+1) WR_REL */
+
#define R1_ILLEGAL_COMMAND (1 << 22)
#define R1_APP_CMD (1 << 5)
@@ -224,6 +237,7 @@
#define MMCPART_NOAVAILABLE (0xff)
#define PART_ACCESS_MASK (0x7)
#define PART_SUPPORT (0x1)
+#define ENHNCD_SUPPORT (0x2)
#define PART_ENH_ATTRIB (0x1f)
/* Maximum block size for MMC */
@@ -302,17 +316,23 @@ struct mmc {
uint csd[4];
uint cid[4];
ushort rca;
+ u8 part_support;
+ u8 part_attr;
+ u8 wr_rel_set;
char part_config;
char part_num;
uint tran_speed;
uint read_bl_len;
uint write_bl_len;
- uint erase_grp_size;
+ uint erase_grp_size; /* in 512-byte sectors */
+ uint hc_wp_grp_size; /* in 512-byte sectors */
u64 capacity;
u64 capacity_user;
u64 capacity_boot;
u64 capacity_rpmb;
u64 capacity_gp[4];
+ u64 enh_user_start;
+ u64 enh_user_size;
block_dev_desc_t block_dev;
char op_cond_pending; /* 1 if we are waiting on an op_cond command */
char init_in_progress; /* 1 if we have done mmc_start_init() */
@@ -321,6 +341,27 @@ struct mmc {
int ddr_mode;
};
+struct mmc_hwpart_conf {
+ struct {
+ uint enh_start; /* in 512-byte sectors */
+ uint enh_size; /* in 512-byte sectors, if 0 no enh area */
+ unsigned wr_rel_change : 1;
+ unsigned wr_rel_set : 1;
+ } user;
+ struct {
+ uint size; /* in 512-byte sectors */
+ unsigned enhanced : 1;
+ unsigned wr_rel_change : 1;
+ unsigned wr_rel_set : 1;
+ } gp_part[4];
+};
+
+enum mmc_hwpart_conf_mode {
+ MMC_HWPART_CONF_CHECK,
+ MMC_HWPART_CONF_SET,
+ MMC_HWPART_CONF_COMPLETE,
+};
+
int mmc_register(struct mmc *mmc);
struct mmc *mmc_create(const struct mmc_config *cfg, void *priv);
void mmc_destroy(struct mmc *mmc);
@@ -333,6 +374,8 @@ int mmc_set_dev(int dev_num);
void print_mmc_devices(char separator);
int get_mmc_num(void);
int mmc_switch_part(int dev_num, unsigned int part_num);
+int mmc_hwpart_config(struct mmc *mmc, const struct mmc_hwpart_conf *conf,
+ enum mmc_hwpart_conf_mode mode);
int mmc_getcd(struct mmc *mmc);
int board_mmc_getcd(struct mmc *mmc);
int mmc_getwp(struct mmc *mmc);