Эх сурвалжийг харах

Merge branch 'master' of git://git.denx.de/u-boot-mmc

Tom Rini 10 жил өмнө
parent
commit
1cd2000698

+ 6 - 0
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)

+ 5 - 0
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

+ 5 - 0
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

+ 5 - 0
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 */

+ 3 - 0
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

+ 168 - 0
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 */

+ 204 - 1
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"

+ 9 - 0
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

+ 1 - 0
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

+ 282 - 25
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 */
@@ -1138,6 +1383,18 @@ static int mmc_startup(struct mmc *mmc)
 			unsigned int extw = ext_csd_bits[idx];
 			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

+ 695 - 0
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;
+}

+ 44 - 1
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);