From 716897760fe41c350f55564a73d2e58dafd1ee53 Mon Sep 17 00:00:00 2001 From: "Ye.Li" Date: Thu, 20 Feb 2014 18:00:57 +0800 Subject: esdhc/usdhc: Fix PIO mode bug in fsl_esdhc driver When configure the fsl_esdhc driver to PIO mode by defining "CONFIG_SYS_FSL_ESDHC_USE_PIO", the SD/MMC read and write will fail. Two bugs in the driver to cause the issue: 1. The read buffer was invalidated after reading from DATAPORT register, which should be only applied to DMA mode. The valid data in cache was overwritten by physical memory. 2. The watermarks are not set in PIO mode, will cause according state not be set. Acked-by: Pantelis Antoniou Signed-off-by: Ye.Li diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 50cba64..dca28f4 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -174,7 +174,7 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) int timeout; struct fsl_esdhc_cfg *cfg = mmc->priv; struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; -#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO + uint wml_value; wml_value = data->blocksize/4; @@ -184,12 +184,15 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) wml_value = WML_RD_WML_MAX_VAL; esdhc_clrsetbits32(®s->wml, WML_RD_WML_MASK, wml_value); +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO esdhc_write32(®s->dsaddr, (u32)data->dest); +#endif } else { +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO flush_dcache_range((ulong)data->src, (ulong)data->src+data->blocks *data->blocksize); - +#endif if (wml_value > WML_WR_WML_MAX) wml_value = WML_WR_WML_MAX_VAL; if ((esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { @@ -199,19 +202,10 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) esdhc_clrsetbits32(®s->wml, WML_WR_WML_MASK, wml_value << 16); +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO esdhc_write32(®s->dsaddr, (u32)data->src); +#endif } -#else /* CONFIG_SYS_FSL_ESDHC_USE_PIO */ - if (!(data->flags & MMC_DATA_READ)) { - if ((esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { - printf("\nThe SD card is locked. " - "Can not write to a locked card.\n\n"); - return TIMEOUT; - } - esdhc_write32(®s->dsaddr, (u32)data->src); - } else - esdhc_write32(®s->dsaddr, (u32)data->dest); -#endif /* CONFIG_SYS_FSL_ESDHC_USE_PIO */ esdhc_write32(®s->blkattr, data->blocks << 16 | data->blocksize); @@ -388,9 +382,10 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) goto out; } } while ((irqstat & DATA_COMPLETE) != DATA_COMPLETE); -#endif + if (data->flags & MMC_DATA_READ) check_and_invalidate_dcache_range(cmd, data); +#endif } out: -- cgit v0.10.2 From 46c8ebc8cddb81e879d08030f030eca53b123ca9 Mon Sep 17 00:00:00 2001 From: Hannes Petermaier Date: Tue, 8 Apr 2014 08:39:20 +0200 Subject: Add board_mmc_init(...) function for init mmc1 only Since B&R boards uses only MMC-Controller #1, it only wastes time if we initialize #0 first to see that there is nothing. Cc: Acked-by: Pantelis Antoniou Signed-off-by: Hannes Petermaier diff --git a/board/BuR/common/common.c b/board/BuR/common/common.c index 4c926ce..25cbe62 100644 --- a/board/BuR/common/common.c +++ b/board/BuR/common/common.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -214,3 +215,9 @@ int board_eth_init(bd_t *bis) return rv; } #endif /* CONFIG_DRIVER_TI_CPSW */ +#if defined(CONFIG_GENERIC_MMC) && !defined(CONFIG_SPL_BUILD) +int board_mmc_init(bd_t *bis) +{ + return omap_mmc_init(1, 0, 0, -1, -1); +} +#endif diff --git a/include/configs/kwb.h b/include/configs/kwb.h index 0f631c0..0860434 100644 --- a/include/configs/kwb.h +++ b/include/configs/kwb.h @@ -109,7 +109,7 @@ #undef CONFIG_ENV_IS_NOWHERE #define CONFIG_ENV_IS_IN_MMC -#define CONFIG_SYS_MMC_ENV_DEV 1 +#define CONFIG_SYS_MMC_ENV_DEV 0 #define CONFIG_SYS_MMC_ENV_PART 2 #define CONFIG_ENV_OFFSET 0x40000 /* TODO: Adresse definieren */ #define CONFIG_ENV_OFFSET_REDUND (CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE) diff --git a/include/configs/tseries.h b/include/configs/tseries.h index 8fb87ac..e550afa 100644 --- a/include/configs/tseries.h +++ b/include/configs/tseries.h @@ -237,7 +237,7 @@ #elif defined(CONFIG_EMMC_BOOT) #undef CONFIG_ENV_IS_NOWHERE #define CONFIG_ENV_IS_IN_MMC -#define CONFIG_SYS_MMC_ENV_DEV 1 +#define CONFIG_SYS_MMC_ENV_DEV 0 #define CONFIG_SYS_MMC_ENV_PART 2 #define CONFIG_ENV_OFFSET 0x40000 /* TODO: Adresse definieren */ #define CONFIG_ENV_OFFSET_REDUND (CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE) -- cgit v0.10.2 From d803fea576c0b9caf2ece1ea1fac0550d3508a4d Mon Sep 17 00:00:00 2001 From: Mateusz Zalega Date: Tue, 29 Apr 2014 20:15:30 +0200 Subject: mmc: postponed needless timer initialization mmc_init() doesn't call get_timer() anymore if MMC is already initialized. Minor formatting fix. Acked-by: Pantelis Antoniou Signed-off-by: Mateusz Zalega diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 16051e5..0c9ae5d 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1310,10 +1310,13 @@ static int mmc_complete_init(struct mmc *mmc) int mmc_init(struct mmc *mmc) { int err = IN_PROGRESS; - unsigned start = get_timer(0); + unsigned start; if (mmc->has_init) return 0; + + start = get_timer(0); + if (!mmc->init_in_progress) err = mmc_start_init(mmc); -- cgit v0.10.2 From 6b2221b008e0ea66de5befab38515e1a38c74d4f Mon Sep 17 00:00:00 2001 From: Andrew Gabbasov Date: Thu, 3 Apr 2014 04:34:32 -0500 Subject: mmc: Handle switch error status bit in MMC card status MMC switch command for unsupported feature (e.g. bus width) sets a switch error bit in card status. This bit should be checked, and, if it's set, no access with new controller settings should be performed. Acked-by: Pantelis Antoniou Signed-off-by: Andrew Gabbasov diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 0c9ae5d..bb9014d 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -150,6 +150,8 @@ int mmc_send_status(struct mmc *mmc, int timeout) #endif return TIMEOUT; } + if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR) + return SWITCH_ERR; return 0; } @@ -501,7 +503,7 @@ static int mmc_change_freq(struct mmc *mmc) err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); if (err) - return err; + return err == SWITCH_ERR ? 0 : err; /* Now check to see that it worked */ err = mmc_send_ext_csd(mmc, ext_csd); diff --git a/include/mmc.h b/include/mmc.h index bc11f45..9143efe 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -54,6 +54,7 @@ #define COMM_ERR -18 /* Communications Error */ #define TIMEOUT -19 #define IN_PROGRESS -20 /* operation is in progress */ +#define SWITCH_ERR -21 /* Card reports failure to switch mode */ #define MMC_CMD_GO_IDLE_STATE 0 #define MMC_CMD_SEND_OP_COND 1 @@ -109,6 +110,7 @@ #define SECURE_ERASE 0x80000000 #define MMC_STATUS_MASK (~0x0206BF7F) +#define MMC_STATUS_SWITCH_ERROR (1 << 7) #define MMC_STATUS_RDY_FOR_DATA (1 << 8) #define MMC_STATUS_CURR_STATE (0xf << 9) #define MMC_STATUS_ERROR (1 << 19) -- cgit v0.10.2 From 91fdabc67aebc2468ad362c02449f215e0971c68 Mon Sep 17 00:00:00 2001 From: Pierre Aubert Date: Thu, 24 Apr 2014 10:30:06 +0200 Subject: eMMC: add support for operations in RPMB partition This patch adds functions for read, write and authentication key programming for the Replay Protected Memory Block partition in the eMMC. Acked-by: Pantelis Antoniou Signed-off-by: Pierre Aubert diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 931922b..4c6ab9e 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_DWMMC) += dw_mmc.o obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o +obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o else diff --git a/drivers/mmc/rpmb.c b/drivers/mmc/rpmb.c new file mode 100644 index 0000000..05936f5 --- /dev/null +++ b/drivers/mmc/rpmb.c @@ -0,0 +1,323 @@ +/* + * Copyright 2014, Staubli Faverges + * Pierre Aubert + * + * eMMC- Replay Protected Memory Block + * According to JEDEC Standard No. 84-A441 + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include "mmc_private.h" + +/* Request codes */ +#define RPMB_REQ_KEY 1 +#define RPMB_REQ_WCOUNTER 2 +#define RPMB_REQ_WRITE_DATA 3 +#define RPMB_REQ_READ_DATA 4 +#define RPMB_REQ_STATUS 5 + +/* Response code */ +#define RPMB_RESP_KEY 0x0100 +#define RPMB_RESP_WCOUNTER 0x0200 +#define RPMB_RESP_WRITE_DATA 0x0300 +#define RPMB_RESP_READ_DATA 0x0400 + +/* Error codes */ +#define RPMB_OK 0 +#define RPMB_ERR_GENERAL 1 +#define RPMB_ERR_AUTH 2 +#define RPMB_ERR_COUNTER 3 +#define RPMB_ERR_ADDRESS 4 +#define RPMB_ERR_WRITE 5 +#define RPMB_ERR_READ 6 +#define RPMB_ERR_KEY 7 +#define RPMB_ERR_CNT_EXPIRED 0x80 +#define RPMB_ERR_MSK 0x7 + +/* Sizes of RPMB data frame */ +#define RPMB_SZ_STUFF 196 +#define RPMB_SZ_MAC 32 +#define RPMB_SZ_DATA 256 +#define RPMB_SZ_NONCE 16 + +#define SHA256_BLOCK_SIZE 64 + +/* Error messages */ +static const char * const rpmb_err_msg[] = { + "", + "General failure", + "Authentication failure", + "Counter failure", + "Address failure", + "Write failure", + "Read failure", + "Authentication key not yet programmed", +}; + + +/* Structure of RPMB data frame. */ +struct s_rpmb { + unsigned char stuff[RPMB_SZ_STUFF]; + unsigned char mac[RPMB_SZ_MAC]; + unsigned char data[RPMB_SZ_DATA]; + unsigned char nonce[RPMB_SZ_NONCE]; + unsigned long write_counter; + unsigned short address; + unsigned short block_count; + unsigned short result; + unsigned short request; +}; + +static int mmc_set_blockcount(struct mmc *mmc, unsigned int blockcount, + bool is_rel_write) +{ + struct mmc_cmd cmd = {0}; + + cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT; + cmd.cmdarg = blockcount & 0x0000FFFF; + if (is_rel_write) + cmd.cmdarg |= 1 << 31; + cmd.resp_type = MMC_RSP_R1; + + return mmc_send_cmd(mmc, &cmd, NULL); +} +static int mmc_rpmb_request(struct mmc *mmc, const struct s_rpmb *s, + unsigned int count, bool is_rel_write) +{ + struct mmc_cmd cmd = {0}; + struct mmc_data data; + int ret; + + ret = mmc_set_blockcount(mmc, count, is_rel_write); + if (ret) { +#ifdef CONFIG_MMC_RPMB_TRACE + printf("%s:mmc_set_blockcount-> %d\n", __func__, ret); +#endif + return 1; + } + + cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1b; + + data.src = (const char *)s; + data.blocks = 1; + data.blocksize = MMC_MAX_BLOCK_LEN; + data.flags = MMC_DATA_WRITE; + + ret = mmc_send_cmd(mmc, &cmd, &data); + if (ret) { +#ifdef CONFIG_MMC_RPMB_TRACE + printf("%s:mmc_send_cmd-> %d\n", __func__, ret); +#endif + return 1; + } + return 0; +} +static int mmc_rpmb_response(struct mmc *mmc, struct s_rpmb *s, + unsigned short expected) +{ + struct mmc_cmd cmd = {0}; + struct mmc_data data; + int ret; + + ret = mmc_set_blockcount(mmc, 1, false); + if (ret) { +#ifdef CONFIG_MMC_RPMB_TRACE + printf("%s:mmc_set_blockcount-> %d\n", __func__, ret); +#endif + return -1; + } + cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1; + + data.dest = (char *)s; + data.blocks = 1; + data.blocksize = MMC_MAX_BLOCK_LEN; + data.flags = MMC_DATA_READ; + + ret = mmc_send_cmd(mmc, &cmd, &data); + if (ret) { +#ifdef CONFIG_MMC_RPMB_TRACE + printf("%s:mmc_send_cmd-> %d\n", __func__, ret); +#endif + return -1; + } + /* Check the response and the status */ + if (be16_to_cpu(s->request) != expected) { +#ifdef CONFIG_MMC_RPMB_TRACE + printf("%s:response= %x\n", __func__, + be16_to_cpu(s->request)); +#endif + return -1; + } + ret = be16_to_cpu(s->result); + if (ret) { + printf("%s %s\n", rpmb_err_msg[ret & RPMB_ERR_MSK], + (ret & RPMB_ERR_CNT_EXPIRED) ? + "Write counter has expired" : ""); + } + + /* Return the status of the command */ + return ret; +} +static int mmc_rpmb_status(struct mmc *mmc, unsigned short expected) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1); + + memset(rpmb_frame, 0, sizeof(struct s_rpmb)); + rpmb_frame->request = cpu_to_be16(RPMB_REQ_STATUS); + if (mmc_rpmb_request(mmc, rpmb_frame, 1, false)) + return -1; + + /* Read the result */ + return mmc_rpmb_response(mmc, rpmb_frame, expected); +} +static void rpmb_hmac(unsigned char *key, unsigned char *buff, int len, + unsigned char *output) +{ + sha256_context ctx; + int i; + unsigned char k_ipad[SHA256_BLOCK_SIZE]; + unsigned char k_opad[SHA256_BLOCK_SIZE]; + + sha256_starts(&ctx); + + /* According to RFC 4634, the HMAC transform looks like: + SHA(K XOR opad, SHA(K XOR ipad, text)) + + where K is an n byte key. + ipad is the byte 0x36 repeated blocksize times + opad is the byte 0x5c repeated blocksize times + and text is the data being protected. + */ + + for (i = 0; i < RPMB_SZ_MAC; i++) { + k_ipad[i] = key[i] ^ 0x36; + k_opad[i] = key[i] ^ 0x5c; + } + /* remaining pad bytes are '\0' XOR'd with ipad and opad values */ + for ( ; i < SHA256_BLOCK_SIZE; i++) { + k_ipad[i] = 0x36; + k_opad[i] = 0x5c; + } + sha256_update(&ctx, k_ipad, SHA256_BLOCK_SIZE); + sha256_update(&ctx, buff, len); + sha256_finish(&ctx, output); + + /* Init context for second pass */ + sha256_starts(&ctx); + + /* start with outer pad */ + sha256_update(&ctx, k_opad, SHA256_BLOCK_SIZE); + + /* then results of 1st hash */ + sha256_update(&ctx, output, RPMB_SZ_MAC); + + /* finish up 2nd pass */ + sha256_finish(&ctx, output); +} +int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *pcounter) +{ + int ret; + ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1); + + /* Fill the request */ + memset(rpmb_frame, 0, sizeof(struct s_rpmb)); + rpmb_frame->request = cpu_to_be16(RPMB_REQ_WCOUNTER); + if (mmc_rpmb_request(mmc, rpmb_frame, 1, false)) + return -1; + + /* Read the result */ + ret = mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_WCOUNTER); + if (ret) + return ret; + + *pcounter = be32_to_cpu(rpmb_frame->write_counter); + return 0; +} +int mmc_rpmb_set_key(struct mmc *mmc, void *key) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1); + /* Fill the request */ + memset(rpmb_frame, 0, sizeof(struct s_rpmb)); + rpmb_frame->request = cpu_to_be16(RPMB_REQ_KEY); + memcpy(rpmb_frame->mac, key, RPMB_SZ_MAC); + + if (mmc_rpmb_request(mmc, rpmb_frame, 1, true)) + return -1; + + /* read the operation status */ + return mmc_rpmb_status(mmc, RPMB_RESP_KEY); +} +int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk, + unsigned short cnt, unsigned char *key) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1); + int i; + + for (i = 0; i < cnt; i++) { + /* Fill the request */ + memset(rpmb_frame, 0, sizeof(struct s_rpmb)); + rpmb_frame->address = cpu_to_be16(blk + i); + rpmb_frame->request = cpu_to_be16(RPMB_REQ_READ_DATA); + if (mmc_rpmb_request(mmc, rpmb_frame, 1, false)) + break; + + /* Read the result */ + if (mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_READ_DATA)) + break; + + /* Check the HMAC if key is provided */ + if (key) { + unsigned char ret_hmac[RPMB_SZ_MAC]; + + rpmb_hmac(key, rpmb_frame->data, 284, ret_hmac); + if (memcmp(ret_hmac, rpmb_frame->mac, RPMB_SZ_MAC)) { + printf("MAC error on block #%d\n", i); + break; + } + } + /* Copy data */ + memcpy(addr + i * RPMB_SZ_DATA, rpmb_frame->data, RPMB_SZ_DATA); + } + return i; +} +int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk, + unsigned short cnt, unsigned char *key) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1); + unsigned long wcount; + int i; + + for (i = 0; i < cnt; i++) { + if (mmc_rpmb_get_counter(mmc, &wcount)) { + printf("Cannot read RPMB write counter\n"); + break; + } + + /* Fill the request */ + memset(rpmb_frame, 0, sizeof(struct s_rpmb)); + memcpy(rpmb_frame->data, addr + i * RPMB_SZ_DATA, RPMB_SZ_DATA); + rpmb_frame->address = cpu_to_be16(blk + i); + rpmb_frame->block_count = cpu_to_be16(1); + rpmb_frame->write_counter = cpu_to_be32(wcount); + rpmb_frame->request = cpu_to_be16(RPMB_REQ_WRITE_DATA); + /* Computes HMAC */ + rpmb_hmac(key, rpmb_frame->data, 284, rpmb_frame->mac); + + if (mmc_rpmb_request(mmc, rpmb_frame, 1, true)) + break; + + /* Get status */ + if (mmc_rpmb_status(mmc, RPMB_RESP_WRITE_DATA)) + break; + } + return i; +} diff --git a/include/mmc.h b/include/mmc.h index 9143efe..a3a100b 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -71,6 +71,7 @@ #define MMC_CMD_SET_BLOCKLEN 16 #define MMC_CMD_READ_SINGLE_BLOCK 17 #define MMC_CMD_READ_MULTIPLE_BLOCK 18 +#define MMC_CMD_SET_BLOCK_COUNT 23 #define MMC_CMD_WRITE_SINGLE_BLOCK 24 #define MMC_CMD_WRITE_MULTIPLE_BLOCK 25 #define MMC_CMD_ERASE_GROUP_START 35 @@ -227,6 +228,7 @@ * boot partitions (2), general purpose partitions (4) in MMC v4.4. */ #define MMC_NUM_BOOT_PARTITION 2 +#define MMC_PART_RPMB 3 /* RPMB partition number */ struct mmc_cid { unsigned long psn; @@ -338,7 +340,13 @@ int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access); int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode); /* Function to modify the RST_n_FUNCTION field of EXT_CSD */ int mmc_set_rst_n_function(struct mmc *mmc, u8 enable); - +/* Functions to read / write the RPMB partition */ +int mmc_rpmb_set_key(struct mmc *mmc, void *key); +int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *counter); +int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk, + unsigned short cnt, unsigned char *key); +int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk, + unsigned short cnt, unsigned char *key); /** * Start device initialization and return immediately; it does not block on * polling OCR (operation condition register) status. Then you should call diff --git a/lib/Makefile b/lib/Makefile index 27e4f78..377ab13 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -35,6 +35,7 @@ obj-y += net_utils.o obj-$(CONFIG_PHYSMEM) += physmem.o obj-y += qsort.o obj-$(CONFIG_SHA1) += sha1.o +obj-$(CONFIG_SUPPORT_EMMC_RPMB) += sha256.o obj-$(CONFIG_SHA256) += sha256.o obj-y += strmhz.o obj-$(CONFIG_TPM) += tpm.o -- cgit v0.10.2 From a5dffa4b67fb0ad635088c9853abf6fcb181ac3c Mon Sep 17 00:00:00 2001 From: Pierre Aubert Date: Thu, 24 Apr 2014 10:30:07 +0200 Subject: Add the function 'confirm_yesno' for interactive User's confirmation is asked in different commands. This commit adds a function for such confirmation. Acked-by: Pantelis Antoniou Signed-off-by: Pierre Aubert diff --git a/common/cmd_fuse.c b/common/cmd_fuse.c index 0df57db..abab978 100644 --- a/common/cmd_fuse.c +++ b/common/cmd_fuse.c @@ -33,15 +33,8 @@ static int confirm_prog(void) "what you are doing!\n" "\nReally perform this fuse programming? \n"); - if (getc() == 'y') { - int c; - - putc('y'); - c = getc(); - putc('\n'); - if (c == '\r') - return 1; - } + if (confirm_yesno()) + return 1; puts("Fuse programming aborted\n"); return 0; diff --git a/common/cmd_nand.c b/common/cmd_nand.c index 04ab0f1..a84f7dc 100644 --- a/common/cmd_nand.c +++ b/common/cmd_nand.c @@ -605,22 +605,16 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) opts.spread = spread; if (scrub) { - if (!scrub_yes) - puts(scrub_warn); - - if (scrub_yes) + if (scrub_yes) { opts.scrub = 1; - else if (getc() == 'y') { - puts("y"); - if (getc() == '\r') + } else { + puts(scrub_warn); + if (confirm_yesno()) { opts.scrub = 1; - else { + } else { puts("scrub aborted\n"); return 1; } - } else { - puts("scrub aborted\n"); - return 1; } } ret = nand_erase_opts(nand, &opts); diff --git a/common/cmd_otp.c b/common/cmd_otp.c index 67808aa..593bb8c 100644 --- a/common/cmd_otp.c +++ b/common/cmd_otp.c @@ -158,21 +158,9 @@ int do_otp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) lowup(half + count - 1), page + (half + count - 1) / 2, half + count ); - - i = 0; - while (1) { - if (tstc()) { - const char exp_ans[] = "YES\r"; - char c; - putc(c = getc()); - if (exp_ans[i++] != c) { - printf(" Aborting\n"); - return 1; - } else if (!exp_ans[i]) { - puts("\n"); - break; - } - } + if (!confirm_yesno()) { + printf(" Aborting\n"); + return 1; } } diff --git a/common/console.c b/common/console.c index 2dfb788..5453726 100644 --- a/common/console.c +++ b/common/console.c @@ -537,7 +537,33 @@ int ctrlc(void) } return 0; } - +/* Reads user's confirmation. + Returns 1 if user's input is "y", "Y", "yes" or "YES" +*/ +int confirm_yesno(void) +{ + int i; + char str_input[5]; + + /* Flush input */ + while (tstc()) + getc(); + i = 0; + while (i < sizeof(str_input)) { + str_input[i] = getc(); + putc(str_input[i]); + if (str_input[i] == '\r') + break; + i++; + } + putc('\n'); + if (strncmp(str_input, "y\r", 2) == 0 || + strncmp(str_input, "Y\r", 2) == 0 || + strncmp(str_input, "yes\r", 4) == 0 || + strncmp(str_input, "YES\r", 4) == 0) + return 1; + return 0; +} /* pass 1 to disable ctrlc() checking, 0 to enable. * returns previous state */ diff --git a/include/common.h b/include/common.h index 13e5dc7..232136c 100644 --- a/include/common.h +++ b/include/common.h @@ -836,7 +836,7 @@ int ctrlc (void); int had_ctrlc (void); /* have we had a Control-C since last clear? */ void clear_ctrlc (void); /* clear the Control-C condition */ int disable_ctrlc (int); /* 1 to disable, 0 to enable Control-C detect */ - +int confirm_yesno(void); /* 1 if input is "y", "Y", "yes" or "YES" */ /* * STDIO based functions (can always be used) */ -- cgit v0.10.2 From 1fd93c6e7d8a1e4d6261058cefab11b875ded252 Mon Sep 17 00:00:00 2001 From: Pierre Aubert Date: Thu, 24 Apr 2014 10:30:08 +0200 Subject: eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command This sub-command adds support for the RPMB partition of an eMMC: * mmc rpmb key
Programs the authentication key in the eMMC This key can not be overwritten. * mmc rpmb read
<#count> [address of key] Reads <#count> blocks of 256 bytes in the RPMB partition beginning at block number . If the optionnal address of the authentication key is provided, the Message Authentication Code (MAC) is verified on each block. * mmc rpmb write
<#count>
Writes <#count> blocks of 256 bytes in the RPMB partition beginning at block number . The datas are signed with the key provided. * mmc rpmb counter Returns the 'Write counter' of the RPMB partition. The sub-command is conditional on compilation flag CONFIG_SUPPORT_EMMC_RPMB Acked-by: Pantelis Antoniou Signed-off-by: Pierre Aubert CC: Wolfgang Denk diff --git a/README b/README index 5f89552..0bd4069 100644 --- a/README +++ b/README @@ -1534,6 +1534,16 @@ The following options need to be configured: CONFIG_SH_MMCIF_CLK Define the clock frequency for MMCIF + CONFIG_GENERIC_MMC + Enable the generic MMC driver + + CONFIG_SUPPORT_EMMC_BOOT + Enable some additional features of the eMMC boot partitions. + + CONFIG_SUPPORT_EMMC_RPMB + Enable the commands for reading, writing and programming the + key for the Replay Protection Memory Block partition in eMMC. + - USB Device Firmware Update (DFU) class support: CONFIG_DFU_FUNCTION This enables the USB portion of the DFU USB class diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index c1916c9..55af295 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -71,12 +71,6 @@ U_BOOT_CMD( ); #else /* !CONFIG_GENERIC_MMC */ -enum mmc_state { - MMC_INVALID, - MMC_READ, - MMC_WRITE, - MMC_ERASE, -}; static void print_mmcinfo(struct mmc *mmc) { printf("Device: %s\n", mmc->cfg->name); @@ -98,7 +92,18 @@ static void print_mmcinfo(struct mmc *mmc) printf("Bus Width: %d-bit\n", mmc->bus_width); } - +static struct mmc *init_mmc_device(int dev) +{ + struct mmc *mmc; + mmc = find_mmc_device(dev); + if (!mmc) { + printf("no mmc device at slot %x\n", dev); + return NULL; + } + if (mmc_init(mmc)) + return NULL; + return mmc; +} static int do_mmcinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { struct mmc *mmc; @@ -112,351 +117,546 @@ static int do_mmcinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } } - mmc = find_mmc_device(curr_device); + mmc = init_mmc_device(curr_device); + if (!mmc) + return CMD_RET_FAILURE; - if (mmc) { - mmc_init(mmc); + print_mmcinfo(mmc); + return CMD_RET_SUCCESS; +} - print_mmcinfo(mmc); - return 0; - } else { - printf("no mmc device at slot %x\n", curr_device); +#ifdef CONFIG_SUPPORT_EMMC_RPMB +static int confirm_key_prog(void) +{ + puts("Warning: Programming authentication key can be done only once !\n" + " Use this command only if you are sure of what you are doing,\n" + "Really perform the key programming? "); + if (confirm_yesno()) return 1; + + puts("Authentication key programming aborted\n"); + return 0; +} +static int do_mmcrpmb_key(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + void *key_addr; + struct mmc *mmc = find_mmc_device(curr_device); + + if (argc != 2) + return CMD_RET_USAGE; + + key_addr = (void *)simple_strtoul(argv[1], NULL, 16); + if (!confirm_key_prog()) + return CMD_RET_FAILURE; + if (mmc_rpmb_set_key(mmc, key_addr)) { + printf("ERROR - Key already programmed ?\n"); + return CMD_RET_FAILURE; } + return CMD_RET_SUCCESS; } +static int do_mmcrpmb_read(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + u16 blk, cnt; + void *addr; + int n; + void *key_addr = NULL; + struct mmc *mmc = find_mmc_device(curr_device); -U_BOOT_CMD( - mmcinfo, 1, 0, do_mmcinfo, - "display MMC info", - "- display info of the current MMC device" -); + if (argc < 4) + return CMD_RET_USAGE; -static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) + addr = (void *)simple_strtoul(argv[1], NULL, 16); + blk = simple_strtoul(argv[2], NULL, 16); + cnt = simple_strtoul(argv[3], NULL, 16); + + if (argc == 5) + key_addr = (void *)simple_strtoul(argv[4], NULL, 16); + + printf("\nMMC RPMB read: dev # %d, block # %d, count %d ... ", + curr_device, blk, cnt); + n = mmc_rpmb_read(mmc, addr, blk, cnt, key_addr); + + printf("%d RPMB blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR"); + if (n != cnt) + return CMD_RET_FAILURE; + return CMD_RET_SUCCESS; +} +static int do_mmcrpmb_write(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) { - enum mmc_state state; + u16 blk, cnt; + void *addr; + int n; + void *key_addr; + struct mmc *mmc = find_mmc_device(curr_device); - if (argc < 2) + if (argc != 5) return CMD_RET_USAGE; - if (curr_device < 0) { - if (get_mmc_num() > 0) - curr_device = 0; - else { - puts("No MMC device available\n"); - return 1; - } + addr = (void *)simple_strtoul(argv[1], NULL, 16); + blk = simple_strtoul(argv[2], NULL, 16); + cnt = simple_strtoul(argv[3], NULL, 16); + key_addr = (void *)simple_strtoul(argv[4], NULL, 16); + + printf("\nMMC RPMB write: dev # %d, block # %d, count %d ... ", + curr_device, blk, cnt); + n = mmc_rpmb_write(mmc, addr, blk, cnt, key_addr); + + printf("%d RPMB blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR"); + if (n != cnt) + return CMD_RET_FAILURE; + return CMD_RET_SUCCESS; +} +static int do_mmcrpmb_counter(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + unsigned long counter; + struct mmc *mmc = find_mmc_device(curr_device); + + if (mmc_rpmb_get_counter(mmc, &counter)) + return CMD_RET_FAILURE; + printf("RPMB Write counter= %lx\n", counter); + return CMD_RET_SUCCESS; +} + +static cmd_tbl_t cmd_rpmb[] = { + U_BOOT_CMD_MKENT(key, 2, 0, do_mmcrpmb_key, "", ""), + U_BOOT_CMD_MKENT(read, 5, 1, do_mmcrpmb_read, "", ""), + U_BOOT_CMD_MKENT(write, 5, 0, do_mmcrpmb_write, "", ""), + U_BOOT_CMD_MKENT(counter, 1, 1, do_mmcrpmb_counter, "", ""), +}; + +static int do_mmcrpmb(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + cmd_tbl_t *cp; + struct mmc *mmc; + char original_part; + int ret; + + cp = find_cmd_tbl(argv[1], cmd_rpmb, ARRAY_SIZE(cmd_rpmb)); + + /* Drop the rpmb subcommand */ + argc--; + argv++; + + if (cp == NULL || argc > cp->maxargs) + return CMD_RET_USAGE; + if (flag == CMD_FLAG_REPEAT && !cp->repeatable) + return CMD_RET_SUCCESS; + + mmc = init_mmc_device(curr_device); + if (!mmc) + return CMD_RET_FAILURE; + + if (!(mmc->version & MMC_VERSION_MMC)) { + printf("It is not a EMMC device\n"); + return CMD_RET_FAILURE; + } + if (mmc->version < MMC_VERSION_4_41) { + printf("RPMB not supported before version 4.41\n"); + return CMD_RET_FAILURE; } + /* Switch to the RPMB partition */ + original_part = mmc->part_num; + if (mmc->part_num != MMC_PART_RPMB) { + if (mmc_switch_part(curr_device, MMC_PART_RPMB) != 0) + return CMD_RET_FAILURE; + mmc->part_num = MMC_PART_RPMB; + } + ret = cp->cmd(cmdtp, flag, argc, argv); - if (strcmp(argv[1], "rescan") == 0) { - struct mmc *mmc; + /* Return to original partition */ + if (mmc->part_num != original_part) { + if (mmc_switch_part(curr_device, original_part) != 0) + return CMD_RET_FAILURE; + mmc->part_num = original_part; + } + return ret; +} +#endif - if (argc != 2) - return CMD_RET_USAGE; +static int do_mmc_read(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct mmc *mmc; + u32 blk, cnt, n; + void *addr; - mmc = find_mmc_device(curr_device); - if (!mmc) { - printf("no mmc device at slot %x\n", curr_device); - return 1; - } + if (argc != 4) + return CMD_RET_USAGE; - mmc->has_init = 0; + addr = (void *)simple_strtoul(argv[1], NULL, 16); + blk = simple_strtoul(argv[2], NULL, 16); + cnt = simple_strtoul(argv[3], NULL, 16); - if (mmc_init(mmc)) - return 1; - else - return 0; - } else if (strcmp(argv[1], "part") == 0) { - block_dev_desc_t *mmc_dev; - struct mmc *mmc; + mmc = init_mmc_device(curr_device); + if (!mmc) + return CMD_RET_FAILURE; - if (argc != 2) - return CMD_RET_USAGE; + printf("\nMMC read: dev # %d, block # %d, count %d ... ", + curr_device, blk, cnt); - mmc = find_mmc_device(curr_device); - if (!mmc) { - printf("no mmc device at slot %x\n", curr_device); - return 1; - } - mmc_init(mmc); - mmc_dev = mmc_get_dev(curr_device); - if (mmc_dev != NULL && - mmc_dev->type != DEV_TYPE_UNKNOWN) { - print_part(mmc_dev); - return 0; - } + n = mmc->block_dev.block_read(curr_device, blk, cnt, addr); + /* flush cache after read */ + flush_cache((ulong)addr, cnt * 512); /* FIXME */ + printf("%d blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR"); - puts("get mmc type error!\n"); - return 1; - } else if (strcmp(argv[1], "list") == 0) { - if (argc != 2) - return CMD_RET_USAGE; - print_mmc_devices('\n'); - return 0; - } else if (strcmp(argv[1], "dev") == 0) { - int dev, part = -1; - struct mmc *mmc; - - if (argc == 2) - dev = curr_device; - else if (argc == 3) - dev = simple_strtoul(argv[2], NULL, 10); - else if (argc == 4) { - dev = (int)simple_strtoul(argv[2], NULL, 10); - part = (int)simple_strtoul(argv[3], NULL, 10); - if (part > PART_ACCESS_MASK) { - printf("#part_num shouldn't be larger" - " than %d\n", PART_ACCESS_MASK); - return 1; - } - } else - return CMD_RET_USAGE; + return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} +static int do_mmc_write(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct mmc *mmc; + u32 blk, cnt, n; + void *addr; - mmc = find_mmc_device(dev); - if (!mmc) { - printf("no mmc device at slot %x\n", dev); - return 1; - } + if (argc != 4) + return CMD_RET_USAGE; - mmc_init(mmc); - if (part != -1) { - int ret; - if (mmc->part_config == MMCPART_NOAVAILABLE) { - printf("Card doesn't support part_switch\n"); - return 1; - } + addr = (void *)simple_strtoul(argv[1], NULL, 16); + blk = simple_strtoul(argv[2], NULL, 16); + cnt = simple_strtoul(argv[3], NULL, 16); - if (part != mmc->part_num) { - ret = mmc_switch_part(dev, part); - if (!ret) - mmc->part_num = part; + mmc = init_mmc_device(curr_device); + if (!mmc) + return CMD_RET_FAILURE; - printf("switch to partitions #%d, %s\n", - part, (!ret) ? "OK" : "ERROR"); - } - } - curr_device = dev; - if (mmc->part_config == MMCPART_NOAVAILABLE) - printf("mmc%d is current device\n", curr_device); - else - printf("mmc%d(part %d) is current device\n", - curr_device, mmc->part_num); + printf("\nMMC write: dev # %d, block # %d, count %d ... ", + curr_device, blk, cnt); - return 0; -#ifdef CONFIG_SUPPORT_EMMC_BOOT - } else if (strcmp(argv[1], "partconf") == 0) { - int dev; - struct mmc *mmc; - u8 ack, part_num, access; - - if (argc == 6) { - dev = simple_strtoul(argv[2], NULL, 10); - ack = simple_strtoul(argv[3], NULL, 10); - part_num = simple_strtoul(argv[4], NULL, 10); - access = simple_strtoul(argv[5], NULL, 10); - } else { - return CMD_RET_USAGE; - } + if (mmc_getwp(mmc) == 1) { + printf("Error: card is write protected!\n"); + return CMD_RET_FAILURE; + } + n = mmc->block_dev.block_write(curr_device, blk, cnt, addr); + printf("%d blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR"); - mmc = find_mmc_device(dev); - if (!mmc) { - printf("no mmc device at slot %x\n", dev); - return 1; - } + return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} +static int do_mmc_erase(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct mmc *mmc; + u32 blk, cnt, n; - if (IS_SD(mmc)) { - puts("PARTITION_CONFIG only exists on eMMC\n"); - return 1; - } + if (argc != 3) + return CMD_RET_USAGE; - /* acknowledge to be sent during boot operation */ - return mmc_set_part_conf(mmc, ack, part_num, access); - } else if (strcmp(argv[1], "bootbus") == 0) { - int dev; - struct mmc *mmc; - u8 width, reset, mode; - - if (argc == 6) { - dev = simple_strtoul(argv[2], NULL, 10); - width = simple_strtoul(argv[3], NULL, 10); - reset = simple_strtoul(argv[4], NULL, 10); - mode = simple_strtoul(argv[5], NULL, 10); - } else { - return CMD_RET_USAGE; - } + blk = simple_strtoul(argv[1], NULL, 16); + cnt = simple_strtoul(argv[2], NULL, 16); - mmc = find_mmc_device(dev); - if (!mmc) { - printf("no mmc device at slot %x\n", dev); - return 1; - } + mmc = init_mmc_device(curr_device); + if (!mmc) + return CMD_RET_FAILURE; - if (IS_SD(mmc)) { - puts("BOOT_BUS_WIDTH only exists on eMMC\n"); - return 1; - } + printf("\nMMC erase: dev # %d, block # %d, count %d ... ", + curr_device, blk, cnt); - /* acknowledge to be sent during boot operation */ - return mmc_set_boot_bus_width(mmc, width, reset, mode); - } else if (strcmp(argv[1], "bootpart-resize") == 0) { - int dev; - struct mmc *mmc; - u32 bootsize, rpmbsize; - - if (argc == 5) { - dev = simple_strtoul(argv[2], NULL, 10); - bootsize = simple_strtoul(argv[3], NULL, 10); - rpmbsize = simple_strtoul(argv[4], NULL, 10); - } else { - return CMD_RET_USAGE; - } + if (mmc_getwp(mmc) == 1) { + printf("Error: card is write protected!\n"); + return CMD_RET_FAILURE; + } + n = mmc->block_dev.block_erase(curr_device, blk, cnt); + printf("%d blocks erased: %s\n", n, (n == cnt) ? "OK" : "ERROR"); - mmc = find_mmc_device(dev); - if (!mmc) { - printf("no mmc device at slot %x\n", dev); - return 1; - } + return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} +static int do_mmc_rescan(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct mmc *mmc; - if (IS_SD(mmc)) { - printf("It is not a EMMC device\n"); - return 1; - } + mmc = find_mmc_device(curr_device); + if (!mmc) { + printf("no mmc device at slot %x\n", curr_device); + return CMD_RET_FAILURE; + } - if (0 == mmc_boot_partition_size_change(mmc, - bootsize, rpmbsize)) { - printf("EMMC boot partition Size %d MB\n", bootsize); - printf("EMMC RPMB partition Size %d MB\n", rpmbsize); - return 0; - } else { - printf("EMMC boot partition Size change Failed.\n"); - return 1; - } - } else if (strcmp(argv[1], "rst-function") == 0) { - /* - * Set the RST_n_ENABLE bit of RST_n_FUNCTION - * The only valid values are 0x0, 0x1 and 0x2 and writing - * a value of 0x1 or 0x2 sets the value permanently. - */ - int dev; - struct mmc *mmc; - u8 enable; - - if (argc == 4) { - dev = simple_strtoul(argv[2], NULL, 10); - enable = simple_strtoul(argv[3], NULL, 10); - } else { - return CMD_RET_USAGE; - } + mmc->has_init = 0; - if (enable > 2 || enable < 0) { - puts("Invalid RST_n_ENABLE value\n"); - return CMD_RET_USAGE; + if (mmc_init(mmc)) + return CMD_RET_FAILURE; + return CMD_RET_SUCCESS; +} +static int do_mmc_part(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + block_dev_desc_t *mmc_dev; + struct mmc *mmc; + + mmc = init_mmc_device(curr_device); + if (!mmc) + return CMD_RET_FAILURE; + + mmc_dev = mmc_get_dev(curr_device); + if (mmc_dev != NULL && mmc_dev->type != DEV_TYPE_UNKNOWN) { + print_part(mmc_dev); + return CMD_RET_SUCCESS; + } + + puts("get mmc type error!\n"); + return CMD_RET_FAILURE; +} +static int do_mmc_dev(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + int dev, part = -1; + struct mmc *mmc; + + if (argc == 1) { + dev = curr_device; + } else if (argc == 2) { + dev = simple_strtoul(argv[1], NULL, 10); + } else if (argc == 3) { + dev = (int)simple_strtoul(argv[1], NULL, 10); + part = (int)simple_strtoul(argv[2], NULL, 10); + if (part > PART_ACCESS_MASK) { + printf("#part_num shouldn't be larger than %d\n", + PART_ACCESS_MASK); + return CMD_RET_FAILURE; } + } else { + return CMD_RET_USAGE; + } - mmc = find_mmc_device(dev); - if (!mmc) { - printf("no mmc device at slot %x\n", dev); - return 1; + mmc = init_mmc_device(dev); + if (!mmc) + return CMD_RET_FAILURE; + + if (part != -1) { + int ret; + if (mmc->part_config == MMCPART_NOAVAILABLE) { + printf("Card doesn't support part_switch\n"); + return CMD_RET_FAILURE; } - if (IS_SD(mmc)) { - puts("RST_n_FUNCTION only exists on eMMC\n"); - return 1; + if (part != mmc->part_num) { + ret = mmc_switch_part(dev, part); + if (!ret) + mmc->part_num = part; + + printf("switch to partitions #%d, %s\n", + part, (!ret) ? "OK" : "ERROR"); } + } + curr_device = dev; + if (mmc->part_config == MMCPART_NOAVAILABLE) + printf("mmc%d is current device\n", curr_device); + else + printf("mmc%d(part %d) is current device\n", + curr_device, mmc->part_num); - return mmc_set_rst_n_function(mmc, enable); -#endif /* CONFIG_SUPPORT_EMMC_BOOT */ + return CMD_RET_SUCCESS; +} +static int do_mmc_list(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + print_mmc_devices('\n'); + return CMD_RET_SUCCESS; +} +#ifdef CONFIG_SUPPORT_EMMC_BOOT +static int do_mmc_bootbus(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + int dev; + struct mmc *mmc; + u8 width, reset, mode; + + if (argc != 5) + return CMD_RET_USAGE; + dev = simple_strtoul(argv[1], NULL, 10); + width = simple_strtoul(argv[2], NULL, 10); + reset = simple_strtoul(argv[3], NULL, 10); + mode = simple_strtoul(argv[4], NULL, 10); + + mmc = init_mmc_device(dev); + if (!mmc) + return CMD_RET_FAILURE; + + if (IS_SD(mmc)) { + puts("BOOT_BUS_WIDTH only exists on eMMC\n"); + return CMD_RET_FAILURE; } - else if (argc == 3 && strcmp(argv[1], "setdsr") == 0) { - struct mmc *mmc = find_mmc_device(curr_device); - u32 val = simple_strtoul(argv[2], NULL, 16); - int ret; + /* acknowledge to be sent during boot operation */ + return mmc_set_boot_bus_width(mmc, width, reset, mode); +} +static int do_mmc_boot_resize(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + int dev; + struct mmc *mmc; + u32 bootsize, rpmbsize; - if (!mmc) { - printf("no mmc device at slot %x\n", curr_device); - return 1; - } - ret = mmc_set_dsr(mmc, val); - printf("set dsr %s\n", (!ret) ? "OK, force rescan" : "ERROR"); - if (!ret) { - mmc->has_init = 0; - if (mmc_init(mmc)) - return 1; - else - return 0; - } - return ret; + if (argc != 4) + return CMD_RET_USAGE; + dev = simple_strtoul(argv[1], NULL, 10); + bootsize = simple_strtoul(argv[2], NULL, 10); + rpmbsize = simple_strtoul(argv[3], NULL, 10); + + mmc = init_mmc_device(dev); + if (!mmc) + return CMD_RET_FAILURE; + + if (IS_SD(mmc)) { + printf("It is not a EMMC device\n"); + return CMD_RET_FAILURE; } - state = MMC_INVALID; - if (argc == 5 && strcmp(argv[1], "read") == 0) - state = MMC_READ; - else if (argc == 5 && strcmp(argv[1], "write") == 0) - state = MMC_WRITE; - else if (argc == 4 && strcmp(argv[1], "erase") == 0) - state = MMC_ERASE; - - if (state != MMC_INVALID) { - struct mmc *mmc = find_mmc_device(curr_device); - int idx = 2; - u32 blk, cnt, n; - void *addr; - - if (state != MMC_ERASE) { - addr = (void *)simple_strtoul(argv[idx], NULL, 16); - ++idx; - } else - addr = NULL; - blk = simple_strtoul(argv[idx], NULL, 16); - cnt = simple_strtoul(argv[idx + 1], NULL, 16); - - if (!mmc) { - printf("no mmc device at slot %x\n", curr_device); - return 1; - } + if (mmc_boot_partition_size_change(mmc, bootsize, rpmbsize)) { + printf("EMMC boot partition Size change Failed.\n"); + return CMD_RET_FAILURE; + } - printf("\nMMC %s: dev # %d, block # %d, count %d ... ", - argv[1], curr_device, blk, cnt); + printf("EMMC boot partition Size %d MB\n", bootsize); + printf("EMMC RPMB partition Size %d MB\n", rpmbsize); + return CMD_RET_SUCCESS; +} +static int do_mmc_partconf(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + int dev; + struct mmc *mmc; + u8 ack, part_num, access; - mmc_init(mmc); + if (argc != 5) + return CMD_RET_USAGE; - if ((state == MMC_WRITE || state == MMC_ERASE)) { - if (mmc_getwp(mmc) == 1) { - printf("Error: card is write protected!\n"); - return 1; - } - } + dev = simple_strtoul(argv[1], NULL, 10); + ack = simple_strtoul(argv[2], NULL, 10); + part_num = simple_strtoul(argv[3], NULL, 10); + access = simple_strtoul(argv[4], NULL, 10); - switch (state) { - case MMC_READ: - n = mmc->block_dev.block_read(curr_device, blk, - cnt, addr); - /* flush cache after read */ - flush_cache((ulong)addr, cnt * 512); /* FIXME */ - break; - case MMC_WRITE: - n = mmc->block_dev.block_write(curr_device, blk, - cnt, addr); - break; - case MMC_ERASE: - n = mmc->block_dev.block_erase(curr_device, blk, cnt); - break; - default: - BUG(); - } + mmc = init_mmc_device(dev); + if (!mmc) + return CMD_RET_FAILURE; + + if (IS_SD(mmc)) { + puts("PARTITION_CONFIG only exists on eMMC\n"); + return CMD_RET_FAILURE; + } + + /* acknowledge to be sent during boot operation */ + return mmc_set_part_conf(mmc, ack, part_num, access); +} +static int do_mmc_rst_func(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + int dev; + struct mmc *mmc; + u8 enable; + + /* + * Set the RST_n_ENABLE bit of RST_n_FUNCTION + * The only valid values are 0x0, 0x1 and 0x2 and writing + * a value of 0x1 or 0x2 sets the value permanently. + */ + if (argc != 3) + return CMD_RET_USAGE; + + dev = simple_strtoul(argv[1], NULL, 10); + enable = simple_strtoul(argv[2], NULL, 10); + + if (enable > 2 || enable < 0) { + puts("Invalid RST_n_ENABLE value\n"); + return CMD_RET_USAGE; + } + + mmc = init_mmc_device(dev); + if (!mmc) + return CMD_RET_FAILURE; + + if (IS_SD(mmc)) { + puts("RST_n_FUNCTION only exists on eMMC\n"); + return CMD_RET_FAILURE; + } - printf("%d blocks %s: %s\n", - n, argv[1], (n == cnt) ? "OK" : "ERROR"); - return (n == cnt) ? 0 : 1; + return mmc_set_rst_n_function(mmc, enable); +} +#endif +static int do_mmc_setdsr(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct mmc *mmc; + u32 val; + int ret; + + if (argc != 2) + return CMD_RET_USAGE; + val = simple_strtoul(argv[2], NULL, 16); + + mmc = find_mmc_device(curr_device); + if (!mmc) { + printf("no mmc device at slot %x\n", curr_device); + return CMD_RET_FAILURE; + } + ret = mmc_set_dsr(mmc, val); + printf("set dsr %s\n", (!ret) ? "OK, force rescan" : "ERROR"); + if (!ret) { + mmc->has_init = 0; + if (mmc_init(mmc)) + return CMD_RET_FAILURE; + else + return CMD_RET_SUCCESS; } + return ret; +} + +static cmd_tbl_t cmd_mmc[] = { + U_BOOT_CMD_MKENT(info, 1, 0, do_mmcinfo, "", ""), + U_BOOT_CMD_MKENT(read, 4, 1, do_mmc_read, "", ""), + U_BOOT_CMD_MKENT(write, 4, 0, do_mmc_write, "", ""), + U_BOOT_CMD_MKENT(erase, 3, 0, do_mmc_erase, "", ""), + U_BOOT_CMD_MKENT(rescan, 1, 1, do_mmc_rescan, "", ""), + 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, "", ""), +#ifdef CONFIG_SUPPORT_EMMC_BOOT + U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""), + U_BOOT_CMD_MKENT(bootpart-resize, 3, 0, do_mmc_boot_resize, "", ""), + U_BOOT_CMD_MKENT(partconf, 5, 0, do_mmc_partconf, "", ""), + U_BOOT_CMD_MKENT(rst-function, 3, 0, do_mmc_rst_func, "", ""), +#endif +#ifdef CONFIG_SUPPORT_EMMC_RPMB + U_BOOT_CMD_MKENT(rpmb, CONFIG_SYS_MAXARGS, 1, do_mmcrpmb, "", ""), +#endif + U_BOOT_CMD_MKENT(setdsr, 2, 0, do_mmc_setdsr, "", ""), +}; + +static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *cp; + + cp = find_cmd_tbl(argv[1], cmd_mmc, ARRAY_SIZE(cmd_mmc)); + + /* Drop the mmc command */ + argc--; + argv++; + + if (cp == NULL || argc > cp->maxargs) + return CMD_RET_USAGE; + if (flag == CMD_FLAG_REPEAT && !cp->repeatable) + return CMD_RET_SUCCESS; - return CMD_RET_USAGE; + if (curr_device < 0) { + if (get_mmc_num() > 0) { + curr_device = 0; + } else { + puts("No MMC device available\n"); + return CMD_RET_FAILURE; + } + } + return cp->cmd(cmdtp, flag, argc, argv); } U_BOOT_CMD( - mmc, 6, 1, do_mmcops, + mmc, 7, 1, do_mmcops, "MMC sub system", - "read addr blk# cnt\n" + "info - display info of the current MMC device\n" + "mmc read addr blk# cnt\n" "mmc write addr blk# cnt\n" "mmc erase blk# cnt\n" "mmc rescan\n" @@ -474,6 +674,20 @@ U_BOOT_CMD( " - Change the RST_n_FUNCTION field of the specified device\n" " WARNING: This is a write-once field and 0 / 1 / 2 are the only valid values.\n" #endif - "mmc setdsr - set DSR register value\n" +#ifdef CONFIG_SUPPORT_EMMC_RPMB + "mmc rpmb read addr blk# cnt [address of auth-key] - block size is 256 bytes\n" + "mmc rpmb write addr blk# cnt
- block size is 256 bytes\n" + "mmc rpmb key
- program the RPMB authentication key.\n" + "mmc rpmb counter - read the value of the write counter\n" +#endif + "mmc setdsr - set DSR register value\n" ); + +/* Old command kept for compatibility. Same as 'mmc info' */ +U_BOOT_CMD( + mmcinfo, 1, 0, do_mmcinfo, + "display MMC info", + "- display info of the current MMC device" +); + #endif /* !CONFIG_GENERIC_MMC */ -- cgit v0.10.2 From 8210a343cd1eb586b72ff396563db15bffed25c5 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 7 May 2014 12:19:00 -0600 Subject: cmd_part: fix typo in part command help text All the sub-commands start with the main command name, but it was missing from one of the help texts. typos fix. Signed-off-by: Stephen Warren Acked-by: Tom Rini Acked-by: Pantelis Antoniou diff --git a/common/cmd_part.c b/common/cmd_part.c index 1424854..c84bc27 100644 --- a/common/cmd_part.c +++ b/common/cmd_part.c @@ -82,7 +82,7 @@ int do_part(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) U_BOOT_CMD( part, 5, 1, do_part, "disk partition related commands", - "uuid :\n" + "part uuid :\n" " - print partition UUID\n" "part uuid : \n" " - set environment variable to partition UUID\n" -- cgit v0.10.2 From 336b6f90482f23b149323b698c05c2713fe55701 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 7 May 2014 12:19:01 -0600 Subject: disk: support devices with HW partitions Some device types (e.g. eMMC) have hardware-level partitions (for eMMC, separate boot and user data partitions). This change allows the user to specify the HW partition they wish to access when passing a device ID to U-Boot Commands such as part, ls, load, ums, etc. The syntax allows an optional ".$hwpartid" to be appended to the device name string for those commands. Existing syntax, for MMC device 0, default HW partition ID, SW partition ID 1: ls mmc 0:1 / New syntax, for MMC device 0, HW partition ID 1 (boot0), SW partition ID 2: ls mmc 0.1:2 / For my purposes, this is most useful for the ums (USB mass storage gadget) command, but there's no reason not to allow the new syntax globally. This patch adds the core support infra-structure. The next patch will provide the implementation for MMC. Acked-by: Pantelis Antoniou Signed-off-by: Stephen Warren diff --git a/disk/part.c b/disk/part.c index b8c6aac..5e10cae 100644 --- a/disk/part.c +++ b/disk/part.c @@ -22,6 +22,7 @@ struct block_drvr { char *name; block_dev_desc_t* (*get_dev)(int dev); + int (*select_hwpart)(int dev_num, int hwpart); }; static const struct block_drvr block_drvr[] = { @@ -52,11 +53,13 @@ static const struct block_drvr block_drvr[] = { DECLARE_GLOBAL_DATA_PTR; #ifdef HAVE_BLOCK_DEVICE -block_dev_desc_t *get_dev(const char *ifname, int dev) +block_dev_desc_t *get_dev_hwpart(const char *ifname, int dev, int hwpart) { const struct block_drvr *drvr = block_drvr; block_dev_desc_t* (*reloc_get_dev)(int dev); + int (*select_hwpart)(int dev_num, int hwpart); char *name; + int ret; if (!ifname) return NULL; @@ -68,17 +71,41 @@ block_dev_desc_t *get_dev(const char *ifname, int dev) while (drvr->name) { name = drvr->name; reloc_get_dev = drvr->get_dev; + select_hwpart = drvr->select_hwpart; #ifdef CONFIG_NEEDS_MANUAL_RELOC name += gd->reloc_off; reloc_get_dev += gd->reloc_off; -#endif - if (strncmp(ifname, name, strlen(name)) == 0) - return reloc_get_dev(dev); + if (select_hwpart) + select_hwpart += gd->reloc_off; +#endif + if (strncmp(ifname, name, strlen(name)) == 0) { + block_dev_desc_t *dev_desc = reloc_get_dev(dev); + if (!dev_desc) + return NULL; + if (hwpart == -1) + return dev_desc; + if (!select_hwpart) + return NULL; + ret = select_hwpart(dev_desc->dev, hwpart); + if (ret < 0) + return NULL; + return dev_desc; + } drvr++; } return NULL; } + +block_dev_desc_t *get_dev(const char *ifname, int dev) +{ + return get_dev_hwpart(ifname, dev, -1); +} #else +block_dev_desc_t *get_dev_hwpart(const char *ifname, int dev, int hwpart) +{ + return NULL; +} + block_dev_desc_t *get_dev(const char *ifname, int dev) { return NULL; @@ -413,25 +440,52 @@ int get_partition_info(block_dev_desc_t *dev_desc, int part return -1; } -int get_device(const char *ifname, const char *dev_str, +int get_device(const char *ifname, const char *dev_hwpart_str, block_dev_desc_t **dev_desc) { char *ep; - int dev; + char *dup_str = NULL; + const char *dev_str, *hwpart_str; + int dev, hwpart; + + hwpart_str = strchr(dev_hwpart_str, '.'); + if (hwpart_str) { + dup_str = strdup(dev_hwpart_str); + dup_str[hwpart_str - dev_hwpart_str] = 0; + dev_str = dup_str; + hwpart_str++; + } else { + dev_str = dev_hwpart_str; + hwpart = -1; + } dev = simple_strtoul(dev_str, &ep, 16); if (*ep) { printf("** Bad device specification %s %s **\n", ifname, dev_str); - return -1; + dev = -1; + goto cleanup; + } + + if (hwpart_str) { + hwpart = simple_strtoul(hwpart_str, &ep, 16); + if (*ep) { + printf("** Bad HW partition specification %s %s **\n", + ifname, hwpart_str); + dev = -1; + goto cleanup; + } } - *dev_desc = get_dev(ifname, dev); + *dev_desc = get_dev_hwpart(ifname, dev, hwpart); if (!(*dev_desc) || ((*dev_desc)->type == DEV_TYPE_UNKNOWN)) { - printf("** Bad device %s %s **\n", ifname, dev_str); - return -1; + printf("** Bad device %s %s **\n", ifname, dev_hwpart_str); + dev = -1; + goto cleanup; } +cleanup: + free(dup_str); return dev; } -- cgit v0.10.2 From d235628434657cf1eba58821445cb5ad88e10763 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 7 May 2014 12:19:02 -0600 Subject: mmc: provide a select_hwpart implementation for get_device() This enables specifying which eMMC HW partition to target for any U-Boot command that uses the generic get_partition() function to parse its command-line arguments. Acked-by: Pantelis Antoniou Signed-off-by: Stephen Warren diff --git a/disk/part.c b/disk/part.c index 5e10cae..2827089 100644 --- a/disk/part.c +++ b/disk/part.c @@ -39,7 +39,11 @@ static const struct block_drvr block_drvr[] = { { .name = "usb", .get_dev = usb_stor_get_dev, }, #endif #if defined(CONFIG_MMC) - { .name = "mmc", .get_dev = mmc_get_dev, }, + { + .name = "mmc", + .get_dev = mmc_get_dev, + .select_hwpart = mmc_select_hwpart, + }, #endif #if defined(CONFIG_SYSTEMACE) { .name = "ace", .get_dev = systemace_get_dev, }, diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index bb9014d..8b53ead 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -552,6 +552,32 @@ static int mmc_set_capacity(struct mmc *mmc, int part_num) return 0; } +int mmc_select_hwpart(int dev_num, int hwpart) +{ + struct mmc *mmc = find_mmc_device(dev_num); + int ret; + + if (!mmc) + return -1; + + if (mmc->part_num == hwpart) + return 0; + + if (mmc->part_config == MMCPART_NOAVAILABLE) { + printf("Card doesn't support part_switch\n"); + return -1; + } + + ret = mmc_switch_part(dev_num, hwpart); + if (ret) + return -1; + + mmc->part_num = hwpart; + + return 0; +} + + int mmc_switch_part(int dev_num, unsigned int part_num) { struct mmc *mmc = find_mmc_device(dev_num); diff --git a/include/part.h b/include/part.h index 53532dc..f2c8c64 100644 --- a/include/part.h +++ b/include/part.h @@ -103,6 +103,7 @@ block_dev_desc_t* sata_get_dev(int dev); block_dev_desc_t* scsi_get_dev(int dev); block_dev_desc_t* usb_stor_get_dev(int dev); block_dev_desc_t* mmc_get_dev(int dev); +int mmc_select_hwpart(int dev_num, int hwpart); block_dev_desc_t* systemace_get_dev(int dev); block_dev_desc_t* mg_disk_get_dev(int dev); block_dev_desc_t *host_get_dev(int dev); @@ -126,6 +127,7 @@ static inline block_dev_desc_t* sata_get_dev(int dev) { return NULL; } static inline block_dev_desc_t* scsi_get_dev(int dev) { return NULL; } static inline block_dev_desc_t* usb_stor_get_dev(int dev) { return NULL; } static inline block_dev_desc_t* mmc_get_dev(int dev) { return NULL; } +static inline int mmc_select_hwpart(int dev_num, int hwpart) { return -1; } static inline block_dev_desc_t* systemace_get_dev(int dev) { return NULL; } static inline block_dev_desc_t* mg_disk_get_dev(int dev) { return NULL; } static inline block_dev_desc_t *host_get_dev(int dev) { return NULL; } -- cgit v0.10.2 From df348d8245922adbb03a3a979429c5e70342973c Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 7 May 2014 12:19:03 -0600 Subject: cmd_mmc: use new mmc_select_hwpart() function The implementation of mmc_select_hwpart() was cribbed from do_mmcops(). Update do_mmcops() to call mmc_select_hwpart() to avoid duplication. Manual patch update due to patch order. Acked-by: Pantelis Antoniou Signed-off-by: Stephen Warren diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index 55af295..eea3375 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -403,7 +403,7 @@ static int do_mmc_part(cmd_tbl_t *cmdtp, int flag, static int do_mmc_dev(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - int dev, part = -1; + int dev, part = -1, ret; struct mmc *mmc; if (argc == 1) { @@ -427,20 +427,11 @@ static int do_mmc_dev(cmd_tbl_t *cmdtp, int flag, return CMD_RET_FAILURE; if (part != -1) { - int ret; - if (mmc->part_config == MMCPART_NOAVAILABLE) { - printf("Card doesn't support part_switch\n"); - return CMD_RET_FAILURE; - } - - if (part != mmc->part_num) { - ret = mmc_switch_part(dev, part); - if (!ret) - mmc->part_num = part; - - printf("switch to partitions #%d, %s\n", - part, (!ret) ? "OK" : "ERROR"); - } + ret = mmc_select_hwpart(dev, part); + printf("switch to partitions #%d, %s\n", + part, (!ret) ? "OK" : "ERROR"); + if (ret) + return 1; } curr_device = dev; if (mmc->part_config == MMCPART_NOAVAILABLE) -- cgit v0.10.2