u-boot-brain/common/cmd_mmc.c
Markus Niebel ab71188ce8 mmc: add setdsr support
The eMMC and the SD-Card specifications describe the optional SET_DSR command.
During measurements at our lab we found that some cards implementing this feature
having really strong driver strengts per default. This can lead to voltage peaks
above the specification of the host on signal edges for data sent from a card to
the host.

Since availability of a given card type may be shorter than the time a certain
hardware will be produced it is useful to have support for this command (Alternative
would be changing termination resistors and adapting the driver strength of the
host to the used card.)

Following proposal for an implementation:

- new field that reflects CSD field DSR_IMP in struct mmc
- new field for design specific DSR value in struct mmc
- board code can set DSR value in mmc struct just after registering an controller
- mmc_startup sends the the stored DSR value before selecting a card, if DSR_IMP is set

Additionally the mmc command is extended to make is possible to play around with different
DSR values.

The concept was tested on a i.MX53 based platform using a Micron eMMC card where the default
DSR is 0x0400 (12mA) but in our design 0x0100 (0x0100) were enough. To use this feature for
instance on a mx53loco one have to add a call to mmc_set_dsr() in board_mmc_init() after
calling fsl_esdhc_initialize() for the eMMC.

Signed-off-by: Markus Niebel <Markus.Niebel@tqs.de>
Acked-by: Pantelis Antoniou <panto@antoniou-consulting.com>
2014-01-09 11:47:51 +02:00

451 lines
10 KiB
C

/*
* (C) Copyright 2003
* Kyle Harris, kharris@nexus-tech.net
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <command.h>
#include <mmc.h>
static int curr_device = -1;
#ifndef CONFIG_GENERIC_MMC
int do_mmc (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int dev;
if (argc < 2)
return CMD_RET_USAGE;
if (strcmp(argv[1], "init") == 0) {
if (argc == 2) {
if (curr_device < 0)
dev = 1;
else
dev = curr_device;
} else if (argc == 3) {
dev = (int)simple_strtoul(argv[2], NULL, 10);
} else {
return CMD_RET_USAGE;
}
if (mmc_legacy_init(dev) != 0) {
puts("No MMC card found\n");
return 1;
}
curr_device = dev;
printf("mmc%d is available\n", curr_device);
} else if (strcmp(argv[1], "device") == 0) {
if (argc == 2) {
if (curr_device < 0) {
puts("No MMC device available\n");
return 1;
}
} else if (argc == 3) {
dev = (int)simple_strtoul(argv[2], NULL, 10);
#ifdef CONFIG_SYS_MMC_SET_DEV
if (mmc_set_dev(dev) != 0)
return 1;
#endif
curr_device = dev;
} else {
return CMD_RET_USAGE;
}
printf("mmc%d is current device\n", curr_device);
} else {
return CMD_RET_USAGE;
}
return 0;
}
U_BOOT_CMD(
mmc, 3, 1, do_mmc,
"MMC sub-system",
"init [dev] - init MMC sub system\n"
"mmc device [dev] - show or set current device"
);
#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->name);
printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24);
printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff);
printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff,
(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
printf("Tran Speed: %d\n", mmc->tran_speed);
printf("Rd Block Len: %d\n", mmc->read_bl_len);
printf("%s version %d.%d\n", IS_SD(mmc) ? "SD" : "MMC",
(mmc->version >> 8) & 0xf, mmc->version & 0xff);
printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No");
puts("Capacity: ");
print_size(mmc->capacity, "\n");
printf("Bus Width: %d-bit\n", mmc->bus_width);
}
static int do_mmcinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
struct mmc *mmc;
if (curr_device < 0) {
if (get_mmc_num() > 0)
curr_device = 0;
else {
puts("No MMC device available\n");
return 1;
}
}
mmc = find_mmc_device(curr_device);
if (mmc) {
mmc_init(mmc);
print_mmcinfo(mmc);
return 0;
} else {
printf("no mmc device at slot %x\n", curr_device);
return 1;
}
}
U_BOOT_CMD(
mmcinfo, 1, 0, do_mmcinfo,
"display MMC info",
"- display info of the current MMC device"
);
#ifdef CONFIG_SUPPORT_EMMC_BOOT
static int boot_part_access(struct mmc *mmc, u8 ack, u8 part_num, u8 access)
{
int err;
err = mmc_boot_part_access(mmc, ack, part_num, access);
if ((err == 0) && (access != 0)) {
printf("\t\t\t!!!Notice!!!\n");
printf("!You must close EMMC boot Partition");
printf("after all images are written\n");
printf("!EMMC boot partition has continuity");
printf("at image writing time.\n");
printf("!So, do not close the boot partition");
printf("before all images are written.\n");
return 0;
} else if ((err == 0) && (access == 0))
return 0;
else if ((err != 0) && (access != 0)) {
printf("EMMC boot partition-%d OPEN Failed.\n", part_num);
return 1;
} else {
printf("EMMC boot partition-%d CLOSE Failed.\n", part_num);
return 1;
}
}
#endif
static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
enum mmc_state state;
if (argc < 2)
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;
}
}
if (strcmp(argv[1], "rescan") == 0) {
struct mmc *mmc;
if (argc != 2)
return CMD_RET_USAGE;
mmc = find_mmc_device(curr_device);
if (!mmc) {
printf("no mmc device at slot %x\n", curr_device);
return 1;
}
mmc->has_init = 0;
if (mmc_init(mmc))
return 1;
else
return 0;
} else if (strncmp(argv[1], "part", 4) == 0) {
block_dev_desc_t *mmc_dev;
struct mmc *mmc;
if (argc != 2)
return CMD_RET_USAGE;
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;
}
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;
mmc = find_mmc_device(dev);
if (!mmc) {
printf("no mmc device at slot %x\n", dev);
return 1;
}
mmc_init(mmc);
if (part != -1) {
int ret;
if (mmc->part_config == MMCPART_NOAVAILABLE) {
printf("Card doesn't support part_switch\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 0;
#ifdef CONFIG_SUPPORT_EMMC_BOOT
} else if ((strcmp(argv[1], "open") == 0) ||
(strcmp(argv[1], "close") == 0)) {
int dev;
struct mmc *mmc;
u8 part_num, access = 0;
if (argc == 4) {
dev = simple_strtoul(argv[2], NULL, 10);
part_num = simple_strtoul(argv[3], NULL, 10);
} else {
return CMD_RET_USAGE;
}
mmc = find_mmc_device(dev);
if (!mmc) {
printf("no mmc device at slot %x\n", dev);
return 1;
}
if (IS_SD(mmc)) {
printf("SD device cannot be opened/closed\n");
return 1;
}
if ((part_num <= 0) || (part_num > MMC_NUM_BOOT_PARTITION)) {
printf("Invalid boot partition number:\n");
printf("Boot partition number cannot be <= 0\n");
printf("EMMC44 supports only 2 boot partitions\n");
return 1;
}
if (strcmp(argv[1], "open") == 0)
access = part_num; /* enable R/W access to boot part*/
else
access = 0; /* No access to boot partition */
/* acknowledge to be sent during boot operation */
return boot_part_access(mmc, 1, part_num, access);
} else if (strcmp(argv[1], "bootpart") == 0) {
int dev;
dev = simple_strtoul(argv[2], NULL, 10);
u32 bootsize = simple_strtoul(argv[3], NULL, 10);
u32 rpmbsize = simple_strtoul(argv[4], NULL, 10);
struct mmc *mmc = find_mmc_device(dev);
if (!mmc) {
printf("no mmc device at slot %x\n", dev);
return 1;
}
if (IS_SD(mmc)) {
printf("It is not a EMMC device\n");
return 1;
}
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;
}
#endif /* CONFIG_SUPPORT_EMMC_BOOT */
}
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;
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;
}
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;
}
printf("\nMMC %s: dev # %d, block # %d, count %d ... ",
argv[1], curr_device, blk, cnt);
mmc_init(mmc);
if ((state == MMC_WRITE || state == MMC_ERASE)) {
if (mmc_getwp(mmc) == 1) {
printf("Error: card is write protected!\n");
return 1;
}
}
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();
}
printf("%d blocks %s: %s\n",
n, argv[1], (n == cnt) ? "OK" : "ERROR");
return (n == cnt) ? 0 : 1;
}
return CMD_RET_USAGE;
}
U_BOOT_CMD(
mmc, 6, 1, do_mmcops,
"MMC sub system",
"read addr blk# cnt\n"
"mmc write addr blk# cnt\n"
"mmc erase blk# cnt\n"
"mmc rescan\n"
"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"
#ifdef CONFIG_SUPPORT_EMMC_BOOT
"mmc open <dev> <boot_partition>\n"
" - Enable boot_part for booting and enable R/W access of boot_part\n"
"mmc close <dev> <boot_partition>\n"
" - Enable boot_part for booting and disable access to boot_part\n"
"mmc bootpart <device num> <boot part size MB> <RPMB part size MB>\n"
" - change sizes of boot and RPMB partitions of specified device\n"
#endif
"mmc setdsr - set DSR register value\n"
);
#endif /* !CONFIG_GENERIC_MMC */