mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-30 16:39:35 +00:00
33ace362fd
Some eMMC chips may need the RST_n_FUNCTION bit set to a non-zero value in order for warm reset of the system to work. Details on this being required will be part of the eMMC datasheet. Also add using this command to the dra7xx README. * Whitespace fix by panto Signed-off-by: Tom Rini <trini@ti.com> Acked-by: Pantelis Antoniou <panto@antoniou-consulting.com>
479 lines
11 KiB
C
479 lines
11 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->cfg->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"
|
|
);
|
|
|
|
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 (strcmp(argv[1], "part") == 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], "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;
|
|
}
|
|
|
|
mmc = find_mmc_device(dev);
|
|
if (!mmc) {
|
|
printf("no mmc device at slot %x\n", dev);
|
|
return 1;
|
|
}
|
|
|
|
if (IS_SD(mmc)) {
|
|
puts("PARTITION_CONFIG only exists on eMMC\n");
|
|
return 1;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
mmc = find_mmc_device(dev);
|
|
if (!mmc) {
|
|
printf("no mmc device at slot %x\n", dev);
|
|
return 1;
|
|
}
|
|
|
|
if (IS_SD(mmc)) {
|
|
puts("BOOT_BUS_WIDTH only exists on eMMC\n");
|
|
return 1;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
} 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;
|
|
}
|
|
|
|
if (enable > 2 || enable < 0) {
|
|
puts("Invalid RST_n_ENABLE value\n");
|
|
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)) {
|
|
puts("RST_n_FUNCTION only exists on eMMC\n");
|
|
return 1;
|
|
}
|
|
|
|
return mmc_set_rst_n_function(mmc, enable);
|
|
#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 bootbus dev boot_bus_width reset_boot_bus_width boot_mode\n"
|
|
" - Set the BOOT_BUS_WIDTH field of the specified device\n"
|
|
"mmc bootpart-resize <dev> <boot part size MB> <RPMB part size MB>\n"
|
|
" - Change sizes of boot and RPMB partitions of specified device\n"
|
|
"mmc partconf dev boot_ack boot_partition partition_access\n"
|
|
" - Change the bits of the PARTITION_CONFIG field of the specified device\n"
|
|
"mmc rst-function dev value\n"
|
|
" - 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"
|
|
);
|
|
#endif /* !CONFIG_GENERIC_MMC */
|