diff --git a/commands/Kconfig b/commands/Kconfig index 6847a55..30d0226 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/commands/mmc_extcsd.c b/commands/mmc_extcsd.c index ad8e9ad..4f566bc 100644 --- a/commands/mmc_extcsd.c +++ b/commands/mmc_extcsd.c @@ -2373,7 +2373,7 @@ u8 *dst; int retval = 0; int opt; - char *devname; + char *devpath; int index = 0; int value = 0; int write_operation = 0; @@ -2411,9 +2411,9 @@ if (optind == argc) return COMMAND_ERROR_USAGE; - devname = argv[optind]; + devpath = argv[optind]; - mci = mci_get_device_by_name(devpath_to_name(devname)); + mci = mci_get_device_by_devpath(devpath); if (mci == NULL) { retval = -ENOENT; goto error; diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c index f71ca53..db3450a 100644 --- a/drivers/mci/imx-esdhc.c +++ b/drivers/mci/imx-esdhc.c @@ -653,20 +653,24 @@ dma_set_mask(dev, DMA_BIT_MASK(32)); host->clk = clk_get(dev, socdata->clkidx); - if (IS_ERR(host->clk)) - return PTR_ERR(host->clk); + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); + goto err_free; + } ret = clk_enable(host->clk); if (ret) { dev_err(dev, "Failed to enable clock: %s\n", strerror(ret)); - return ret; + goto err_clk_put; } host->dev = dev; iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); + if (IS_ERR(iores)) { + ret = PTR_ERR(iores); + goto err_clk_disable; + } host->regs = IOMEM(iores->start); caps = esdhc_read32(host, SDHCI_CAPABILITIES); @@ -709,7 +713,21 @@ dev->priv = host; - return mci_register(&host->mci); + ret = mci_register(&host->mci); + if (ret) + goto err_release_res; + + return 0; + +err_release_res: + release_region(iores); +err_clk_disable: + clk_disable(host->clk); +err_clk_put: + clk_put(host->clk); +err_free: + free(host); + return ret; } static struct esdhc_soc_data esdhc_imx25_data = { diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c index 67257bc..9e39cbb 100644 --- a/drivers/mci/mci-core.c +++ b/drivers/mci/mci-core.c @@ -1819,6 +1819,10 @@ host->supply = regulator_get(host->hw_dev, "vmmc"); if (IS_ERR(host->supply)) { + if (host->supply == ERR_PTR(-EPROBE_DEFER)) { + ret = -EPROBE_DEFER; + goto err_free; + } dev_err(&mci->dev, "Failed to get 'vmmc' regulator.\n"); host->supply = NULL; } diff --git a/include/mci.h b/include/mci.h index 77625ea..a45d744 100644 --- a/include/mci.h +++ b/include/mci.h @@ -28,6 +28,7 @@ #include #include +#include #include /* These codes should be sorted numerically in order of newness. If the last @@ -305,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 */ @@ -491,4 +499,9 @@ struct mci *mci_get_device_by_name(const char *name); +static inline struct mci *mci_get_device_by_devpath(const char *devpath) +{ + return mci_get_device_by_name(devpath_to_name(devpath)); +} + #endif /* _MCI_H_ */