[PATCH v2 2/2] mci: implement command to switch a mmc device to enhanced mode
Sascha Hauer
s.hauer at pengutronix.de
Mon Jan 22 00:12:34 PST 2018
On Fri, Jan 19, 2018 at 02:28:25PM +0100, Uwe Kleine-König wrote:
> The command structure allows adding more subcommands and is designed to
> match the Linux program mmc from the mmc-utils. So later more commands
> can easily be added if need be.
>
> Compared to mmc-utils'
>
> mmc enh_area set <-y|-n|-c> <start KiB> <length KiB> <device>
>
> the command that is implemented here (
>
> mmc enh_area setmax <-y|-n|-c> <device>
>
> ) is easier to use (because you don't have to check the maximal allowed
> size by reading some registers and calculate the available size from
> them (which then must be calculated back to register values by the mmc
> command)) but less flexible as it doesn't allow all the crazy
> possibilities specified in the eMMC standard but just creates an
> enhanced area with maximal size.
>
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig at pengutronix.de>
> ---
> commands/Kconfig | 5 ++
> commands/Makefile | 1 +
> commands/mmc.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> include/mci.h | 7 +++
> 4 files changed, 184 insertions(+)
> create mode 100644 commands/mmc.c
>
> diff --git a/commands/Kconfig b/commands/Kconfig
> index ae2dc4b0947b..1b29365a06f2 100644
> --- a/commands/Kconfig
> +++ b/commands/Kconfig
> @@ -238,6 +238,11 @@ config CMD_VERSION
>
> 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
Could you mention that this command is designed to control eMMC cards
like the mmc_extcsd command, but on a higher abstraction level, similar
to the userspace mmc utility. Just something like that, to give the user
a hint what the difference between both commands is?
> +
> config CMD_MMC_EXTCSD
> tristate
> prompt "read/write eMMC ext. CSD register"
> diff --git a/commands/Makefile b/commands/Makefile
> index 37486dceb181..bcd0665cfe88 100644
> --- a/commands/Makefile
> +++ b/commands/Makefile
> @@ -120,6 +120,7 @@ obj-$(CONFIG_CMD_DHCP) += dhcp.o
> 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
Experience shows that we want to use such code independent of commands
some day. Could you put the flesh of the subcommands somewhere else, maybe
to drivers/mci/?
> new file mode 100644
> index 000000000000..2f5c7ad69a37
> --- /dev/null
> +++ b/commands/mmc.c
> @@ -0,0 +1,171 @@
> +#include <command.h>
> +#include <mci.h>
> +#include <stdio.h>
> +#include <string.h>
> +
> +/* enh_area setmax <-y|-n|-c> /dev/mmcX */
> +static int do_mmc_enh_area(int argc, char *argv[])
> +{
> + char *devname;
> + struct mci *mci;
> + u8 *ext_csd;
> + int set_completed = 0;
> + int ret;
> +
> + if (argc != 4 || strcmp(argv[1], "setmax") ||
> + argv[2][0] != '-' ||
> + (argv[2][1] != 'y' && argv[2][1] != 'n' && argv[2][1] != 'c')) {
> + printf("Usage: mmc enh_area setmax <-y|-n|-c> /dev/mmcX\n");
> + return 1;
> + }
I assume 'y' means 'yes', 'n' means 'no', but what does 'c' mean? It
doesn't seem to be implemented and none of these options is documented.
> +
> + if (argv[2][1] == 'y')
> + set_completed = 1;
> +
> + devname = argv[3];
> + if (!strncmp(devname, "/dev/", 5))
> + devname += 5;
> +
> + mci = mci_get_device_by_name(devname);
> + if (!mci) {
> + printf("Failure to open %s as mci device\n", devname);
> + return -ENOENT;
> + }
This part is probably needed by all other future subcommands aswell.
Should it be an extra function?
> + /* get extcsd */
> + ext_csd = xmalloc(512);
> +
> + ret = mci_send_ext_csd(mci, ext_csd);
> + if (ret) {
> + printf("Failure to read EXT_CSD register\n");
> + goto error;
> + }
Also for reusability for other subcommands at would be good to allocate and fill
in ext_csd somewhere else.
> +
> + if (!(ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & EXT_CSD_ENH_ATTRIBUTE_EN_MASK)) {
> + printf("Device doesn't support enhanced area\n");
> + ret = -EIO;
> + goto error;
> + }
> +
> + if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED]) {
> + printf("Partitioning already finalized\n");
> + ret = -EIO;
> + goto error;
> + }
> +
> + ret = mci_switch(mci, EXT_CSD_ERASE_GROUP_DEF, 1);
> + if (ret) {
> + printf("Failure to write to EXT_CSD_ERASE_GROUP_DEF\n");
> + goto error;
This command is *very* verbose in the error path. Would it be enough to
just write the register number of the failed access instead of the name?
> + }
> +
> + ret = mci_switch(mci, EXT_CSD_ENH_START_ADDR, 0);
> + if (ret) {
> + printf("Failure to write to EXT_CSD_ENH_START_ADDR[0]\n");
> + goto error;
> + }
> +
> + ret = mci_switch(mci, EXT_CSD_ENH_START_ADDR + 1, 0);
> + if (ret) {
> + printf("Failure to write to EXT_CSD_ENH_START_ADDR[1]\n");
> + goto error;
> + }
> +
> + ret = mci_switch(mci, EXT_CSD_ENH_START_ADDR + 2, 0);
> + if (ret) {
> + printf("Failure to write to EXT_CSD_ENH_START_ADDR[2]\n");
> + goto error;
> + }
> +
> + ret = mci_switch(mci, EXT_CSD_ENH_START_ADDR + 3, 0);
> + if (ret) {
> + printf("Failure to write to EXT_CSD_ENH_START_ADDR[3]\n");
> + goto error;
> + }
> +
> + ret = mci_switch(mci, EXT_CSD_ENH_SIZE_MULT, ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT]);
> + if (ret) {
> + printf("Failure to write to EXT_CSD_ENH_SIZE_MULT[0]\n");
> + goto error;
> + }
> +
> + ret = mci_switch(mci, EXT_CSD_ENH_SIZE_MULT + 1, ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT + 1]);
> + if (ret) {
> + printf("Failure to write to EXT_CSD_ENH_SIZE_MULT[1]\n");
> + goto error;
> + }
> +
> + ret = mci_switch(mci, EXT_CSD_ENH_SIZE_MULT + 2, ext_csd[EXT_CSD_MAX_ENH_SIZE_MULT + 2]);
> + if (ret) {
> + printf("Failure to write to EXT_CSD_ENH_SIZE_MULT[2]\n");
> + goto error;
> + }
> +
> + ret = mci_switch(mci, EXT_CSD_PARTITIONS_ATTRIBUTE, ext_csd[EXT_CSD_PARTITIONS_ATTRIBUTE] | EXT_CSD_ENH_USR_MASK);
> + if (ret) {
> + printf("Failure to write to EXT_CSD_PARTITIONS_ATTRIBUTE\n");
> + goto error;
> + }
> +
> + if (set_completed) {
> + ret = mci_switch(mci, EXT_CSD_PARTITION_SETTING_COMPLETED, 1);
> + if (ret) {
> + printf("Failure to write to EXT_CSD_PARTITION_SETTING_COMPLETED\n");
> +error:
> + free(ext_csd);
> + } else {
> + printf("Now power cycle the device to let it reconfigure itself.\n");
> + }
> + }
You lose memory when the 'y' option is not given.
> +
> + return ret;
> +}
> +
> +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, subcmdlen;
> + int (*func)(int argc, char *argv[]) = NULL;
> +
> + if (argc < 2) {
> + printf("mmc: required subcommand missing\n");
> + return 1;
> + }
> +
> + subcmdlen = strlen(argv[1]);
> + for (i = 0; i < ARRAY_SIZE(mmc_subcmds); ++i) {
> + if (strncmp(mmc_subcmds[i].cmd, argv[1], subcmdlen) == 0) {
> + if (subcmdlen == strlen(mmc_subcmds[i].cmd)) {
> + /* exact match */
> + func = mmc_subcmds[i].func;
> + break;
> + } else if (func) {
> + printf("mmc: ambiguously abbreviated subcommand");
> + return 1;
> + } else {
> + func = mmc_subcmds[i].func;
> + }
> + }
> + }
Do we really need abbreviated subcommands here? abbreviated command
shouldn't be used in script and I hope this command does not become a
command that people use that frequently that they miss abbreviated
command support. I would just drop it.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
More information about the barebox
mailing list