diff --git a/commands/Kconfig b/commands/Kconfig index e03110f..1e7e72f 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -233,6 +233,17 @@ barebox 2014.05.0-00142-gb289373 #177 Mon May 12 20:35:55 CEST 2014 +config CMD_MMC + tristate + prompt "mmc command allowing to set enhanced area" + depends on MCI + help + Configure mmc cards similar to the userspace mmc utility. Compared to + mmc_extcsd it works on a higher abstraction level. + + Currently only the enh_area subcommand is implemented to configure + the "Enhanced Area" of an mmc device. + config CMD_MMC_EXTCSD tristate prompt "read/write eMMC ext. CSD register" diff --git a/commands/Makefile b/commands/Makefile index 5cd35b7..62fbf1a 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_CMD_BOOTCHOOSER) += bootchooser.o obj-$(CONFIG_CMD_DHRYSTONE) += dhrystone.o obj-$(CONFIG_CMD_SPD_DECODE) += spd_decode.o +obj-$(CONFIG_CMD_MMC) += mmc.o obj-$(CONFIG_CMD_MMC_EXTCSD) += mmc_extcsd.o obj-$(CONFIG_CMD_NAND_BITFLIP) += nand-bitflip.o obj-$(CONFIG_CMD_SEED) += seed.o diff --git a/commands/mmc.c b/commands/mmc.c new file mode 100644 index 0000000..c696e7b --- /dev/null +++ b/commands/mmc.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include + +static int mmc_enh_area_setmax(struct mci *mci, u8 *ext_csd) +{ + unsigned i; + struct { + unsigned index; + unsigned value; + } regval[] = { + { + .index = EXT_CSD_ERASE_GROUP_DEF, + .value = 1, + }, { + .index = EXT_CSD_ENH_START_ADDR, + .value = 0, + }, { + .index = EXT_CSD_ENH_START_ADDR + 1, + .value = 0, + }, { + .index = EXT_CSD_ENH_START_ADDR + 2, + .value = 0, + }, { + .index = EXT_CSD_ENH_START_ADDR + 3, + .value = 0, + }, { + .index = EXT_CSD_ENH_SIZE_MULT, + .value = ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT], + }, { + .index = EXT_CSD_ENH_SIZE_MULT + 1, + .value = ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT + 1], + }, { + .index = EXT_CSD_ENH_SIZE_MULT + 2, + .value = ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT + 2], + }, { + .index = EXT_CSD_PARTITIONS_ATTRIBUTE, + .value = ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR_MASK, + } + }; + + for (i = 0; i < ARRAY_SIZE(regval); ++i) { + int ret = mci_switch(mci, regval[i].index, regval[i].value); + if (ret) { + printf("Failure to write to register %u", regval[i].index); + return ret; + } + } + + return 0; +} + +static int mmc_partitioning_complete(struct mci *mci) +{ + int ret; + + ret = mci_switch(mci, EXT_CSD_PARTITION_SETTING_COMPLETED, 1); + if (ret) + printf("Failure to write to EXT_CSD_PARTITION_SETTING_COMPLETED\n"); + + return ret; +} + +static u8 *mci_get_ext_csd(struct mci *mci) +{ + u8 *ext_csd; + int ret; + + ext_csd = xmalloc(512); + + ret = mci_send_ext_csd(mci, ext_csd); + if (ret) { + printf("Failure to read EXT_CSD register\n"); + free(ext_csd); + return ERR_PTR(-EIO); + } + + return ext_csd; +} + +/* enh_area [-c] /dev/mmcX */ +static int do_mmc_enh_area(int argc, char *argv[]) +{ + const char *devpath; + struct mci *mci; + u8 *ext_csd; + int set_completed = 0; + int opt; + int ret; + + while ((opt = getopt(argc, argv, "c")) > 0) { + switch (opt) { + case 'c': + set_completed = 1; + break; + } + } + + if (argc - optind != 1) { + printf("Usage: mmc enh_area [-c] /dev/mmcX\n"); + return COMMAND_ERROR_USAGE; + } + + devpath = argv[optind]; + + mci = mci_get_device_by_devpath(devpath); + if (!mci) { + printf("Failure to open %s as mci device\n", devpath); + return COMMAND_ERROR; + } + + ext_csd = mci_get_ext_csd(mci); + if (IS_ERR(ext_csd)) + return COMMAND_ERROR; + + if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN_MASK)) { + printf("Device doesn't support enhanced area\n"); + goto error; + } + + if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) { + printf("Partitioning already finalized\n"); + goto error; + } + + ret = mmc_enh_area_setmax(mci, ext_csd); + if (ret) + goto error; + + free(ext_csd); + + if (set_completed) { + ret = mmc_partitioning_complete(mci); + if (ret) + return COMMAND_ERROR; + printf("Now power cycle the device to let it reconfigure itself.\n"); + } + + return COMMAND_SUCCESS; + +error: + free(ext_csd); + return COMMAND_ERROR; +} + +static struct { + const char *cmd; + int (*func)(int argc, char *argv[]); +} mmc_subcmds[] = { + { + .cmd = "enh_area", + .func = do_mmc_enh_area, + } +}; + +static int do_mmc(int argc, char *argv[]) +{ + size_t i; + int (*func)(int argc, char *argv[]) = NULL; + + if (argc < 2) { + printf("mmc: required subcommand missing\n"); + return 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_subcmds); ++i) { + if (strcmp(mmc_subcmds[i].cmd, argv[1]) == 0) { + func = mmc_subcmds[i].func; + break; + } + } + + if (func) { + return func(argc - 1, argv + 1); + } else { + printf("mmc: subcommand \"%s\" not found\n", argv[1]); + return COMMAND_ERROR_USAGE; + } +} + +BAREBOX_CMD_HELP_START(mmc) +BAREBOX_CMD_HELP_TEXT("Modifies mmc properties.") +BAREBOX_CMD_HELP_TEXT("") +BAREBOX_CMD_HELP_TEXT("The subcommand enh_area creates an enhanced area of") +BAREBOX_CMD_HELP_TEXT("maximal size.") +BAREBOX_CMD_HELP_TEXT("Note, with -c this is an irreversible action.") +BAREBOX_CMD_HELP_OPT("-c", "complete partitioning") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(mmc) + .cmd = do_mmc, + BAREBOX_CMD_OPTS("enh_area [-c] /dev/mmcX") + BAREBOX_CMD_HELP(cmd_mmc_help) +BAREBOX_CMD_END diff --git a/include/mci.h b/include/mci.h index a46239d..a45d744 100644 --- a/include/mci.h +++ b/include/mci.h @@ -306,6 +306,13 @@ #define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at 200MHz */ /* SDR mode @1.2V I/O */ +/* register PARTITIONS_ATTRIBUTE [156] */ +#define EXT_CSD_ENH_USR_MASK (1 << 0) + +/* register PARTITIONING_SUPPORT [160] */ +#define EXT_CSD_ENH_ATTRIBUTE_EN_MASK (1 << 0) + +/* register BUS_WIDTH [183], field Bus Mode Selection [4:0] */ #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ #define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */