blob: 67d9aa1cd314d554dd06e9a1e7ab2d41e5e7ee0b [file] [log] [blame]
/*
* (C) Copyright 2003
* Kyle Harris, kharris@nexus-tech.net
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <malloc.h>
#include <command.h>
#include <linux/ctype.h>
#include <mmc.h>
#include <partition_table.h>
#include <emmc_partitions.h>
#include <asm/arch/cpu_sdio.h>
#include <asm/arch/sd_emmc.h>
#include <linux/sizes.h>
#include <asm/cpu_id.h>
extern int mmc_key_erase(void);
extern int find_dev_num_by_partition_name (char *name);
extern int mmc_get_ext_csd(struct mmc *mmc, u8 *ext_csd);
extern int mmc_set_ext_csd(struct mmc *mmc, u8 index, u8 value);
extern void mmc_write_cali_mattern(void *addr);
/* info system. */
#define dtb_err(fmt, ...) printf( "%s()-%d: " fmt , \
__func__, __LINE__, ##__VA_ARGS__)
#define dtb_wrn(fmt, ...) printf( "%s()-%d: " fmt , \
__func__, __LINE__, ##__VA_ARGS__)
/* for detail debug info */
#define dtb_info(fmt, ...) printf( "%s()-%d: " fmt , \
__func__, __LINE__, ##__VA_ARGS__)
struct aml_dtb_rsv {
u8 data[DTB_BLK_SIZE*DTB_BLK_CNT - 4*sizeof(u32)];
u32 magic;
u32 version;
u32 timestamp;
u32 checksum;
};
struct aml_dtb_info {
u32 stamp[2];
u8 valid[2];
};
#define stamp_after(a,b) ((int)(b) - (int)(a) < 0)
/* glb dtb infos */
static struct aml_dtb_info dtb_infos = {{0, 0}, {0, 0}};
#define CONFIG_SECURITYKEY
#if !defined(CONFIG_SYS_MMC_BOOT_DEV)
#define CONFIG_SYS_MMC_BOOT_DEV (CONFIG_SYS_MMC_ENV_DEV)
#endif
#define GXB_START_BLK 0
#define GXL_START_BLK 1
#define UBOOT_SIZE (0x1000) // uboot size 2MB
int info_disprotect = 0;
bool emmckey_is_protected (struct mmc *mmc)
{
#ifdef CONFIG_STORE_COMPATIBLE
#ifdef CONFIG_SECURITYKEY
if (info_disprotect & DISPROTECT_KEY) { // disprotect
printf("emmckey_is_protected : disprotect\n ");
return 0;
}else{
printf("emmckey_is_protected : protect\n ");
// protect
return 1;
}
#else
return 0;
#endif
#else
#ifdef CONFIG_SECURITYKEY
//return mmc->key_protect;
return 0; /* fixme, */
#else
return 0;
#endif
#endif
}
unsigned emmc_cur_partition = 0;
int mmc_read_status(struct mmc *mmc, int timeout)
{
struct mmc_cmd cmd;
int err, retries = 5;
int status;
cmd.cmdidx = MMC_CMD_SEND_STATUS;
cmd.resp_type = MMC_RSP_R1;
if (!mmc_host_is_spi(mmc))
cmd.cmdarg = mmc->rca << 16;
do {
err = mmc_send_cmd(mmc, &cmd, NULL);
if (!err) {
if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) &&
(cmd.response[0] & MMC_STATUS_CURR_STATE) !=
MMC_STATE_PRG)
break;
else if (cmd.response[0] & MMC_STATUS_MASK) {
printf("Status Error: 0x%08X\n",
cmd.response[0]);
return COMM_ERR;
}
} else if (--retries < 0)
return err;
udelay(1000);
} while (timeout--);
status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9;
printf("CURR STATE:%d, status = 0x%x\n", status, cmd.response[0]);
if (timeout <= 0) {
printf("read status Timeout waiting card ready\n");
return TIMEOUT;
}
if (cmd.response[0] & MMC_STATUS_SWITCH_ERROR) {
printf("mmc status swwitch error status =0x%x\n", status);
return SWITCH_ERR;
}
return 0;
}
static int get_off_size(struct mmc * mmc, char * name, uint64_t offset, uint64_t size, u64 * blk, u64 * cnt, u64 * sz_byte)
{
struct partitions *part_info = NULL;
uint64_t off = 0;
int blk_shift = 0;
blk_shift = ffs(mmc->read_bl_len) - 1;
// printf("blk_shift:%d , off:0x%llx , size:0x%llx.\n ",blk_shift,off,size );
part_info = find_mmc_partition_by_name(name);
if (part_info == NULL) {
printf("get partition info failed !!\n");
return -1;
}
off = part_info->offset + offset;
// printf("part_info->offset:0x%llx , off:0x%llx , size:0x%llx.\n",part_info->offset ,off,size);
*blk = off >> blk_shift ;
*cnt = size >> blk_shift ;
*sz_byte = size - ((*cnt)<<blk_shift) ;
// printf("get_partition_off_size : blk:0x%llx , cnt:0x%llx.\n",*blk,*cnt);
return 0;
}
static int get_partition_size(unsigned char* name, uint64_t* addr)
{
struct partitions *part_info = NULL;
part_info = find_mmc_partition_by_name((char *)name);
if (part_info == NULL) {
printf("get partition info failed !!\n");
return -1;
}
*addr = part_info->size >> 9; // unit: 512 bytes
return 0;
}
static inline int isstring(char *p)
{
char *endptr = p;
while (*endptr != '\0') {
if (!(((*endptr >= '0') && (*endptr <= '9'))
|| ((*endptr >= 'a') && (*endptr <= 'f'))
|| ((*endptr >= 'A') && (*endptr <= 'F'))
|| (*endptr == 'x') || (*endptr == 'X')))
return 1;
endptr++;
}
return 0;
}
int do_amlmmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int rc = 0;
/*printf("%s:%d\n",__func__,__LINE__);*/
/*printf("argc = %d\n",argc);*/
switch (argc) {
case 3:
if (strcmp(argv[1], "rescan") == 0) {
int dev = simple_strtoul(argv[2], NULL, 10);
if (dev < 0) {
printf("Cannot find dev.\n");
return 1;
}
struct mmc *mmc = find_mmc_device(dev);
if (!mmc)
return 1;
return mmc_init(mmc);
} else if (strncmp(argv[1], "part", 4) == 0) {
int dev = simple_strtoul(argv[2], NULL, 10);
block_dev_desc_t *mmc_dev;
struct mmc *mmc = find_mmc_device(dev);
if (!mmc) {
puts("no mmc devices available\n");
return 1;
}
mmc_init(mmc);
mmc_dev = mmc_get_dev(dev);
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], "erase") == 0) {
char *name = NULL;
int dev;
u32 n=0;
bool is_part = false;//is argv[2] partition name
bool protect_cache = false;
bool non_loader = false;
int blk_shift;
u64 cnt=0, blk =0,start_blk =0;
struct partitions *part_info;
if (isstring(argv[2])) {
if (!strcmp(argv[2], "whole")) {
name = "logo";
dev = find_dev_num_by_partition_name (name);
}else if(!strcmp(argv[2], "non_cache")){
name = "logo";
dev = find_dev_num_by_partition_name (name);
protect_cache = true;
}
else if(!strcmp(argv[2], "non_loader")){
dev = 1;
non_loader = true;
}
else{
name = argv[2];
dev = find_dev_num_by_partition_name (name);
is_part = true;
}
}else if(isdigit(argv[2][0])){
dev = simple_strtoul(argv[2], NULL, 10);
}else{
printf("Input is invalid, nothing happen.\n");
return 1;
}
if (dev < 0) {
printf("Cannot find dev.\n");
return 1;
}
struct mmc *mmc = find_mmc_device(dev);
if (!mmc)
return 1;
mmc_init(mmc);
blk_shift = ffs(mmc->read_bl_len) -1;
if (is_part) { // erase only one partition
if (emmckey_is_protected(mmc)
&& (strncmp(name, MMC_RESERVED_NAME, sizeof(MMC_RESERVED_NAME)) == 0x00)) {
printf("\"%s-partition\" is been protecting and should no be erased!\n", MMC_RESERVED_NAME);
return 1;
}
part_info = find_mmc_partition_by_name(name);
if (part_info == NULL) {
return 1;
}
blk = part_info->offset>> blk_shift;
if (emmc_cur_partition && !strncmp(name, "bootloader", strlen("bootloader"))) {
cnt = mmc->boot_size>> blk_shift;
}
else
cnt = part_info->size>> blk_shift;
n = mmc->block_dev.block_erase(dev, blk, cnt);
} else { // erase the whole card if possible
if (non_loader) {
part_info = find_mmc_partition_by_name(MMC_BOOT_NAME);
if (part_info == NULL) {
start_blk = 0;
printf("no uboot partition for eMMC boot, just erase from 0\n");
}
else{
start_blk = (part_info->offset + part_info->size) >> blk_shift;
}
}
else{
start_blk = 0;
}
if (emmckey_is_protected(mmc)) {
part_info = find_mmc_partition_by_name(MMC_RESERVED_NAME);
if (part_info == NULL) {
return 1;
}
blk = part_info->offset;
if (blk > 0) { // it means: there should be other partitions before reserve-partition.
blk -= PARTITION_RESERVED;
}
blk >>= blk_shift;
blk -= start_blk;
n=0;
// (1) erase all the area before reserve-partition
if (blk > 0) {
n = mmc->block_dev.block_erase(dev, start_blk, blk);
// printf("(1) erase blk: 0 --> %llx %s\n", blk, (n == 0) ? "OK" : "ERROR");
}
if (n == 0) { // not error
// (2) erase all the area after reserve-partition
if (protect_cache) {
part_info = find_mmc_partition_by_name(MMC_CACHE_NAME);
if (part_info == NULL) {
return 1;
}
}
start_blk = (part_info->offset + part_info->size + PARTITION_RESERVED) >> blk_shift;
u64 erase_cnt = (mmc->capacity >> blk_shift) - 1 - start_blk;
n = mmc->block_dev.block_erase(dev, start_blk, erase_cnt);
// printf("(2) erase blk: %#llx --> %#llx %s\n", start_blk, start_blk+erase_cnt, (n == 0) ? "OK" : "ERROR");
}
} else {
n = mmc->block_dev.block_erase(dev, start_blk, 0); // erase the whole card
}
//erase boot partition
if (mmc->boot_size && (n == 0) && (non_loader == false)) {
for (cnt=0;cnt<2;cnt++) {
rc = mmc_switch_part(dev, cnt+1);
if (rc != 0) {
printf("mmc switch %s failed\n", (cnt == 0)?"boot0":"boot1");
break;
}
n = mmc->block_dev.block_erase(dev, 0, mmc->boot_size>>blk_shift);
if (n != 0) {
printf("mmc erase %s failed\n", (cnt == 0)?"boot0":"boot1");
break;
}
}
rc = mmc_switch_part(dev, 0);
if (rc != 0) {
printf("mmc switch back to user failed\n");
}
}
}
// printf("dev # %d, %s, # %#llx blocks erased %s\n",
// dev, (is_part == 0) ? "card":(argv[2]) ,
// (cnt == 0) ? (int)(mmc->block_dev.lba): cnt ,
// (n == 0) ? "OK" : "ERROR");
return (n == 0) ? 0 : 1;
} else if (strcmp(argv[1], "status") == 0) {
int dev = simple_strtoul(argv[2], NULL, 10);
if (dev < 0) {
printf("Cannot find dev.\n");
return 1;
}
struct mmc *mmc = find_mmc_device(dev);
if (!mmc)
return 1;
if (!mmc->has_init) {
printf("mmc dev %d has not been initialed\n", dev);
return 1;
}
rc = mmc_read_status(mmc, 1000);
if (rc)
return 1;
else
return 0;
} else if (strcmp(argv[1], "response") == 0) {
int dev = simple_strtoul(argv[2], NULL, 10);
if (dev < 0) {
printf("Cannot find dev.\n");
return 1;
}
struct mmc *mmc = find_mmc_device(dev);
if (!mmc)
return 1;
if (!mmc->has_init) {
printf("mmc dev %d has not been initialed\n", dev);
return 1;
}
struct aml_card_sd_info *aml_priv = mmc->priv;
struct sd_emmc_global_regs *sd_emmc_reg = aml_priv->sd_emmc_reg;
printf("last cmd = %d, response0 = 0x%x\n",
(sd_emmc_reg->gcmd_cfg & 0x3f), sd_emmc_reg->gcmd_rsp0);
return 0;
} else if (strcmp(argv[1], "controller") == 0) {
int dev = simple_strtoul(argv[2], NULL, 10);
if (dev < 0) {
printf("Cannot find dev.\n");
return 1;
}
struct mmc *mmc = find_mmc_device(dev);
if (!mmc)
return 1;
struct aml_card_sd_info *aml_priv = mmc->priv;
struct sd_emmc_global_regs *sd_emmc_reg = aml_priv->sd_emmc_reg;
printf("sd_emmc_reg->gclock = 0x%x\n", sd_emmc_reg->gclock);
printf("sd_emmc_reg->gdelay = 0x%x\n", sd_emmc_reg->gdelay);
printf("sd_emmc_reg->gadjust = 0x%x\n", sd_emmc_reg->gadjust);
printf("sd_emmc_reg->gcalout = 0x%x\n", sd_emmc_reg->gcalout);
if (!mmc->has_init) {
printf("mmc dev %d has not been initialed\n", dev);
return 1;
}
printf("sd_emmc_reg->gstart = 0x%x\n", sd_emmc_reg->gstart);
printf("sd_emmc_reg->gcfg = 0x%x\n", sd_emmc_reg->gcfg);
printf("sd_emmc_reg->gstatus = 0x%x\n", sd_emmc_reg->gstatus);
printf("sd_emmc_reg->girq_en = 0x%x\n", sd_emmc_reg->girq_en);
printf("sd_emmc_reg->gcmd_cfg = 0x%x\n", sd_emmc_reg->gcmd_cfg);
printf("sd_emmc_reg->gcmd_arg = 0x%x\n", sd_emmc_reg->gcmd_arg);
printf("sd_emmc_reg->gcmd_dat = 0x%x\n", sd_emmc_reg->gcmd_dat);
printf("sd_emmc_reg->gcmd_rsp0 = 0x%x\n", sd_emmc_reg->gcmd_rsp0);
printf("sd_emmc_reg->gcmd_rsp1 = 0x%x\n", sd_emmc_reg->gcmd_rsp1);
printf("sd_emmc_reg->gcmd_rsp2 = 0x%x\n", sd_emmc_reg->gcmd_rsp2);
printf("sd_emmc_reg->gcmd_rsp3 = 0x%x\n", sd_emmc_reg->gcmd_rsp3);
return 0;
} else {
return cmd_usage(cmdtp);
}
case 0:
case 1:
return CMD_RET_USAGE;
case 4:
if (strcmp(argv[1], "switch") == 0) {
int dev = simple_strtoul(argv[2], NULL, 10);
struct mmc* mmc = find_mmc_device(dev);
if (!mmc) {
puts("no mmc devices available\n");
return 1;
}
mmc_init(mmc);
printf("mmc switch to ");
if (strcmp(argv[3], "boot0") == 0) {
rc = mmc_switch_part(dev, 1);
if (rc == 0) {
emmc_cur_partition = 1;
printf("boot0 success\n");
} else {
printf("boot0 failed\n");
}
}
else if(strcmp(argv[3], "boot1") == 0) {
rc = mmc_switch_part(dev, 2);
if (rc == 0) {
emmc_cur_partition = 2;
printf("boot1 success\n");
} else {
printf("boot1 failed\n");
}
}
else if(strcmp(argv[3], "user") == 0) {
rc = mmc_switch_part(dev, 0);
if (rc == 0) {
emmc_cur_partition = 0;
printf("user success\n");
} else {
printf("user failed\n");
}
}
#ifdef CONFIG_SUPPORT_EMMC_RPMB
else if(strcmp(argv[3], "rpmb") == 0) {
rc = mmc_switch_part(dev, 3);
if (rc == 0) {
emmc_cur_partition = 3;
printf("rpmb success\n");
} else {
printf("rpmb failed\n");
}
}
#endif
else
printf("%s failed\n", argv[3]);
return rc;
} else if (strcmp(argv[1], "ext_csd") == 0) {
int ret;
u8 ext_csd[512] = {0};
int dev = simple_strtoul(argv[2], NULL, 10);
int bit = simple_strtoul(argv[3], NULL, 10);
if ((bit > 511) || (bit < 0)) {
printf("bit is out of area!\n");
return 1;
}
struct mmc* mmc = find_mmc_device(dev);
if (!mmc) {
puts("no mmc devices available\n");
return 1;
}
mmc_init(mmc);
ret = mmc_get_ext_csd(mmc, ext_csd);
printf("read EXT_CSD bit[%d] val[0x%x] %s\n",
bit, ext_csd[bit], (ret == 0) ? "ok" : "fail");
return ret;
} else if (strcmp(argv[1], "size") == 0) {
char *name;
uint64_t* addr =NULL;
name = argv[2];
addr = (uint64_t *)simple_strtoul(argv[3], NULL, 16);
if (!strcmp(name, "wholeDev")) {
int dev = CONFIG_SYS_MMC_BOOT_DEV;
struct mmc* mmc = find_mmc_device(dev);
if (!mmc) {
puts("no mmc devices available\n");
return 1;
}
mmc_init(mmc);
*addr = mmc->capacity >> 9; // unit: 512 bytes
return 0;
}
return get_partition_size((unsigned char *)name, addr);
}
return cmd_usage(cmdtp);
case 2:
if (!strcmp(argv[1], "list")) {
print_mmc_devices('\n');
return 0;
}
if (strcmp(argv[1], "env") == 0) {
printf("herh\n");
env_relocate();
return 0 ;
}
#ifdef CONFIG_SECURITYKEY
if (strcmp(argv[1], "key") == 0) {
struct mmc* mmc;
//char *name = "logo";
int dev = CONFIG_SYS_MMC_BOOT_DEV;
mmc = find_mmc_device(dev);
if (!mmc) {
printf("device %d is invalid\n",dev);
return 1;
}
//mmc->key_protect = 0;
#ifdef CONFIG_STORE_COMPATIBLE
info_disprotect |= DISPROTECT_KEY; //disprotect
printf("emmc disprotect key\n");
#endif
return 0;
}
#endif
return cmd_usage(cmdtp);
default: /* at least 5 args */
if (strcmp(argv[1], "read") == 0) {
int dev;
void *addr =NULL;
u32 flag =0;
u64 cnt =0,n =0, blk =0, sz_byte =0;
char *name=NULL;
u64 offset =0,size =0;
if (argc != 6) {
printf("Input is invalid, nothing happen.\n");
return 1;
}
if (isstring(argv[2])) {
name = argv[2];
dev = find_dev_num_by_partition_name (name);
addr = (void *)simple_strtoul(argv[3], NULL, 16);
size = simple_strtoull(argv[5], NULL, 16);
offset = simple_strtoull(argv[4], NULL, 16);
/*printf("offset %llx size %llx\n",offset,size);*/
flag = 1;
if ((strcmp(argv[2], "card") == 0)) {
flag = 2;
}
}else{
dev = simple_strtoul(argv[2], NULL, 10);
addr = (void *)simple_strtoul(argv[3], NULL, 16);
cnt = simple_strtoull(argv[5], NULL, 16);
blk = simple_strtoull(argv[4], NULL, 16);
}
if (dev < 0) {
printf("Cannot find dev.\n");
return 1;
}
struct mmc *mmc = find_mmc_device(dev);
if (!mmc) {
printf("dev = %d;, no mmc device found",dev);
return 1;
}
if (flag == 1) { // emmc or tsd
/*printf("offset %#llx size %#llx\n",offset,size);*/
get_off_size(mmc, name, offset, size, &blk, &cnt, &sz_byte);
}
else if(flag == 2){ // card
int blk_shift = ffs( mmc->read_bl_len) -1;
cnt = size >> blk_shift;
blk = offset >> blk_shift;
sz_byte = size - (cnt<<blk_shift);
}
/*printf("MMC read: dev # %d, block # %#llx, count # %#llx ...\n",*/
/*dev, blk, cnt);*/
mmc_init(mmc);
n = mmc->block_dev.block_read(dev, blk, cnt, addr);
//read sz_byte bytes
if ((n == cnt) && (sz_byte != 0)) {
/*printf("sz_byte=%#llx bytes\n",sz_byte);*/
void *addr_tmp = malloc(mmc->read_bl_len);
void *addr_byte = (void *)(addr+cnt*(mmc->read_bl_len));
ulong start_blk = blk+cnt;
if (addr_tmp == NULL) {
printf("mmc read: malloc fail\n");
return 1;
}
if (mmc->block_dev.block_read(dev, start_blk, 1, addr_tmp) != 1) { // read 1 block
free(addr_tmp);
printf("mmc read 1 block fail\n");
return 1;
}
memcpy(addr_byte, addr_tmp, sz_byte);
free(addr_tmp);
}
/* flush cache after read */
//flush_cache((ulong)addr, cnt * 512); /* FIXME */
//printf("MMC read: dev # %d, block # %#llx, count # %#llx, byte_size # %#llx %s!\n",
// dev, blk, cnt, sz_byte, (n==cnt) ? "OK" : "ERROR");
return (n == cnt) ? 0 : 1;
} else if (strcmp(argv[1], "write") == 0) {
int dev;
void *addr =NULL;
u32 flag =0;
u64 cnt =0,n =0, blk =0,sz_byte =0;
char *name=NULL;
u64 offset =0,size =0;
cpu_id_t cpu_id = get_cpu_id();
if (argc != 6) {
printf("Input is invalid, nothing happen.\n");
return 1;
}
if (isstring(argv[2])) {
name = argv[2];
if (strcmp(name, "bootloader") == 0)
dev = CONFIG_SYS_MMC_BOOT_DEV;
else
dev = find_dev_num_by_partition_name (name);
addr = (void *)simple_strtoul(argv[3], NULL, 16);
offset = simple_strtoull(argv[4], NULL, 16);
size = simple_strtoull(argv[5], NULL, 16);
flag = 1;
if ((strcmp(argv[2], "card") == 0)) {
flag = 2;
}
}else{
dev = simple_strtoul(argv[2], NULL, 10);
addr = (void *)simple_strtoul(argv[3], NULL, 16);
blk = simple_strtoull(argv[4], NULL, 16);
cnt = simple_strtoull(argv[5], NULL, 16);
}
if (dev < 0) {
printf("Cannot find dev.\n");
return 1;
}
struct mmc *mmc = find_mmc_device(dev);
if (flag == 1) { // tsd or emmc
if (strcmp(name, "bootloader") == 0) {
cnt = UBOOT_SIZE;
if (cpu_id.family_id >= MESON_CPU_MAJOR_ID_GXL) {
blk = GXL_START_BLK;
cnt -= GXL_START_BLK;
}
else
blk = GXB_START_BLK;
sz_byte = 0;
} else
get_off_size(mmc, name, offset, size, &blk, &cnt, &sz_byte);
}
else if(flag == 2){ // card
int blk_shift = ffs( mmc->read_bl_len) -1;
cnt = size >> blk_shift;
blk = offset >> blk_shift;
sz_byte = size - (cnt<<blk_shift);
}
if (!mmc)
return 1;
//printf("MMC write: dev # %d, block # %#llx, count # %#llx ... ",
//dev, blk, cnt);
mmc_init(mmc);
n = mmc->block_dev.block_write(dev, blk, cnt, addr);
//write sz_byte bytes
if ((n == cnt) && (sz_byte != 0)) {
// printf("sz_byte=%#llx bytes\n",sz_byte);
void *addr_tmp = malloc(mmc->write_bl_len);
void *addr_byte = (void*)(addr+cnt*(mmc->write_bl_len));
ulong start_blk = blk+cnt;
if (addr_tmp == NULL) {
printf("mmc write: malloc fail\n");
return 1;
}
if (mmc->block_dev.block_read(dev, start_blk, 1, addr_tmp) != 1) { // read 1 block
free(addr_tmp);
printf("mmc read 1 block fail\n");
return 1;
}
memcpy(addr_tmp, addr_byte, sz_byte);
if (mmc->block_dev.block_write(dev, start_blk, 1, addr_tmp) != 1) { // write 1 block
free(addr_tmp);
printf("mmc write 1 block fail\n");
return 1;
}
free(addr_tmp);
}
//printf("%#llx blocks , %#llx bytes written: %s\n", n, sz_byte, (n==cnt) ? "OK" : "ERROR");
return (n == cnt) ? 0 : 1;
}
else if (strcmp(argv[1], "erase") == 0) {
int dev=0;
u32 flag=0;
u64 cnt = 0, blk = 0, n = 0, sz_byte =0;
char *name=NULL;
u64 offset_addr =0, size=0;
if (argc != 5) {
printf("Input is invalid, nothing happen.\n");
return 1;
}
if (isstring(argv[2])) {
name = argv[2];
dev = find_dev_num_by_partition_name (name);
offset_addr = simple_strtoull(argv[3], NULL, 16);
size = simple_strtoull(argv[4], NULL, 16);
flag = 1;
if ((strcmp(argv[2], "card") == 0)) {
flag = 2;
}
}else if(isdigit(argv[2][0])){
dev = simple_strtoul(argv[2], NULL, 10);
blk = simple_strtoull(argv[3], NULL, 16);
cnt = simple_strtoull(argv[4], NULL, 16);
}
if (dev < 0) {
printf("Cannot find dev.\n");
return 1;
}
struct mmc *mmc = find_mmc_device(dev);
if (flag == 1) { // mmc write logo add offset size
struct partitions *part_info = find_mmc_partition_by_name(name);
if (offset_addr >= part_info->size) {
printf("Start address out #%s# partition'address region,(addr_byte < 0x%llx)\n",
name, part_info->size);
return 1;
}
if ((offset_addr+size) > part_info->size) {
printf("End address exceeds #%s# partition,(offset = 0x%llx,size = 0x%llx)\n",
name, part_info->offset,part_info->size);
return 1;
}
get_off_size(mmc, name, offset_addr, size, &blk, &cnt, &sz_byte);
}
else if(flag == 2){
int tmp_shift = ffs( mmc->read_bl_len) -1;
cnt = size >> tmp_shift;
blk = offset_addr >> tmp_shift;
sz_byte = size - (cnt<<tmp_shift);
}
if (!mmc)
return 1;
printf("MMC erase: dev # %d, start_erase_address(in block) # %#llx, several blocks # %lld will be erased ...\n ",
dev, blk, cnt);
mmc_init(mmc);
if (cnt != 0)
n = mmc->block_dev.block_erase(dev, blk, cnt);
printf("dev # %d, %s, several blocks erased %s\n",
dev, (flag == 0) ? " ":(argv[2]),(n == 0) ? "OK" : "ERROR");
return (n == 0) ? 0 : 1;
} else if (strcmp(argv[1], "ext_csd") == 0) {
int ret;
int dev = simple_strtoul(argv[2], NULL, 10);
int bit = simple_strtoul(argv[3], NULL, 10);
int val = simple_strtoul(argv[4], NULL, 16);
if ((bit > 191) || (bit < 0)) {
printf("bit is not able to write!\n");
return 1;
}
struct mmc* mmc = find_mmc_device(dev);
if (!mmc) {
puts("no mmc devices available\n");
return 1;
}
mmc_init(mmc);
ret = mmc_set_ext_csd(mmc, bit, val);
printf("write EXT_CSD bit[%d] val[0x%x] %s\n",
bit, val, (ret == 0) ? "ok" : "fail");
return ret;
} else
rc = cmd_usage(cmdtp);
return rc;
}
}
U_BOOT_CMD(
amlmmc, 6, 1, do_amlmmcops,
"AMLMMC sub system",
"read <partition_name> ram_addr addr_byte# cnt_byte\n"
"amlmmc write <partition_name> ram_addr addr_byte# cnt_byte\n"
"amlmmc erase <partition_name> addr_byte# cnt_byte\n"
"amlmmc erase <partition_name>/<device num>\n"
"amlmmc rescan <device_num>\n"
"amlmmc part <device_num> - show partition infomation of mmc\n"
"amlmmc list - lists available devices\n"
"amlmmc switch <device_num> <part name> - part name : boot0, boot1, user\n"
"amlmmc status <device_num> - read sd/emmc device status\n"
"amlmmc ext_csd <bit> <value> - read/write sd/emmc device EXT_CSD [bit] value\n"
"amlmmc response <device_num> - read sd/emmc last command response\n"
"amlmmc controller <device_num> - read sd/emmc controller register\n");
/* dtb read&write operation with backup updates */
static u32 _calc_dtb_checksum(struct aml_dtb_rsv * dtb)
{
int i = 0;
int size = sizeof(struct aml_dtb_rsv) - sizeof(u32);
u32 * buffer;
u32 checksum = 0;
if ((u64)dtb % 4 != 0) {
BUG();
}
size = size >> 2;
buffer = (u32*) dtb;
while (i < size)
checksum += buffer[i++];
return checksum;
}
static int _verify_dtb_checksum(struct aml_dtb_rsv * dtb)
{
u32 checksum;
checksum = _calc_dtb_checksum(dtb);
dtb_info("calc %x, store %x\n", checksum, dtb->checksum);
return !(checksum == dtb->checksum);
}
static int _dtb_read(struct mmc *mmc, u64 blk, u64 cnt, void * addr)
{
int dev = EMMC_DTB_DEV;
u64 n;
n = mmc->block_dev.block_read(dev, blk, cnt, addr);
if (n != cnt) {
dtb_err("%s: dev # %d, block # %#llx, count # %#llx ERROR!\n",
__func__, dev, blk, cnt);
}
return (n != cnt);
}
static int _dtb_write(struct mmc *mmc, u64 blk, u64 cnt, void * addr)
{
int dev = EMMC_DTB_DEV;
u64 n;
n = mmc->block_dev.block_write(dev, blk, cnt, addr);
if (n != cnt) {
dtb_err("%s: dev # %d, block # %#llx, count # %#llx ERROR!\n",
__func__, dev, blk, cnt);
}
return (n != cnt);
}
static struct mmc *_dtb_init(void)
{
struct mmc *mmc = find_mmc_device(EMMC_DTB_DEV);
if (!mmc) {
dtb_err("not find mmc\n");
return NULL;
}
if (mmc_init(mmc)) {
dtb_err("mmc init failed\n");
return NULL;
}
return mmc;
}
static int dtb_read_shortcut(struct mmc * mmc, void *addr)
{
u64 blk, cnt, dtb_glb_offset;
int dev = EMMC_DTB_DEV;
struct aml_dtb_info *info = &dtb_infos;
struct partitions * part = NULL;
struct virtual_partition *vpart = NULL;
vpart = aml_get_virtual_partition_by_name(MMC_DTB_NAME);
part = aml_get_partition_by_name(MMC_RESERVED_NAME);
dtb_glb_offset = part->offset + vpart->offset;
/* short cut */
if (info->valid[0]) {
dtb_info("short cut in...\n");
blk = dtb_glb_offset / mmc->read_bl_len;
cnt = vpart->size / mmc->read_bl_len;
if (_dtb_read(mmc, blk, cnt, addr)) {
dtb_err("%s: dev # %d, block # %#llx,cnt # %#llx ERROR!\n",
__func__, dev, blk, cnt);
/*try dtb2 if it's valid */
if (info->valid[1]) {
blk = (dtb_glb_offset + vpart->size) / mmc->read_bl_len;
cnt = vpart->size / mmc->read_bl_len;
if (_dtb_read(mmc, blk, cnt, addr)) {
dtb_err("%s: dev # %d, block # %#llx, cnt # %#llx ERROR!\n",
__func__, dev, blk, cnt);
return -1;
}
}
}
return 0;
}
return -2;
}
int dtb_read(void *addr)
{
int ret = 0, dev = EMMC_DTB_DEV;
u64 blk, cnt, dtb_glb_offset;
struct aml_dtb_rsv * dtb = (struct aml_dtb_rsv *) addr;
struct aml_dtb_info *info = &dtb_infos;
int cpy = 1, valid = 0;
struct mmc * mmc;
struct partitions * part = NULL;
struct virtual_partition *vpart = NULL;
vpart = aml_get_virtual_partition_by_name(MMC_DTB_NAME);
part = aml_get_partition_by_name(MMC_RESERVED_NAME);
dtb_glb_offset = part->offset + vpart->offset;
mmc = _dtb_init();
if (NULL == mmc)
return -10;
if (0 == dtb_read_shortcut(mmc, addr))
return ret;
/* read dtb2 1st, for compatibility without checksum. */
while (cpy >= 0) {
blk = (dtb_glb_offset + cpy * (vpart->size)) / mmc->read_bl_len;
cnt = vpart->size / mmc->read_bl_len;
if (_dtb_read(mmc, blk, cnt, addr)) {
dtb_err("%s: dev # %d, block # %#llx, cnt # %#llx ERROR!\n",
__func__, dev, blk, cnt);
} else {
ret = _verify_dtb_checksum(dtb);
/* check magic avoid whole 0 issue */
if (!ret && (dtb->magic != 0)) {
info->stamp[cpy] = dtb->timestamp;
info->valid[cpy] = 1;
}
else
dtb_wrn("cpy %d is not valid\n", cpy);
}
valid += info->valid[cpy];
cpy --;
}
dtb_info("total valid %d\n", valid);
/* check valid */
switch (valid) {
/* none is valid, using the 1st one for compatibility*/
case 0:
ret = -1;
goto _out;
break;
/* only 1 is valid, using the valid one */
case 1:
if (info->valid[1]) {
blk = (dtb_glb_offset + vpart->size) / mmc->read_bl_len;
if (_dtb_read(mmc, blk, cnt, addr)) {
dtb_err("%s: dev # %d, block # %#llx,cnt # %#llx ERROR!\n",
__func__, dev, blk, cnt);
ret = -2;
}
/* fixme, update the invalid one - dtb1 */
blk = (dtb_glb_offset) / mmc->read_bl_len;
if (_dtb_write(mmc, blk, cnt, addr)) {
dtb_err("%s: dev # %d, block # %#llx,cnt # %#llx ERROR!\n",
__func__, dev, blk, cnt);
ret = -4;
}
info->valid[0] = 1;
info->stamp[0] = dtb->timestamp;
ret = 0;
} else {
dtb_info("update dtb2");
blk = (dtb_glb_offset + vpart->size) / mmc->read_bl_len;
if (_dtb_write(mmc, blk, cnt, addr)) {
dtb_err("%s: dev # %d, block # %#llx,cnt # %#llx ERROR!\n",
__func__, dev, blk, cnt);
ret = -2;
}
info->valid[1] = 1;
info->stamp[1] = dtb->timestamp;
}
break;
/* both are valid, pickup new one. */
case 2:
if (stamp_after(info->stamp[1], info->stamp[0])) {
blk = (dtb_glb_offset + vpart->size) / mmc->read_bl_len;
if (_dtb_read(mmc, blk, cnt, addr)) {
dtb_err("%s: dev # %d, block # %#llx,cnt # %#llx ERROR!\n",
__func__, dev, blk, cnt);
ret = -3;
}
/*update dtb1*/
blk = dtb_glb_offset / mmc->read_bl_len;
if (_dtb_write(mmc, blk, cnt, addr)) {
dtb_err("%s: dev # %d, block # %#llx,cnt # %#llx ERROR!\n",
__func__, dev, blk, cnt);
ret = -3;
}
info->stamp[0] = dtb->timestamp;
ret = 0;
} else if (stamp_after(info->stamp[0], info->stamp[1])) {
/*update dtb2*/
blk = (dtb_glb_offset + vpart->size) / mmc->read_bl_len;
if (_dtb_write(mmc, blk, cnt, addr)) {
dtb_err("%s: dev # %d, block # %#llx,cnt # %#llx ERROR!\n",
__func__, dev, blk, cnt);
ret = -3;
}
info->stamp[1] = dtb->timestamp;
} else {
dtb_info("do nothing\n");
}
break;
default:
dtb_err("impossble valid values.\n");
BUG();
break;
}
_out:
return ret;
}
int dtb_write(void *addr)
{
int ret = 0;
struct aml_dtb_rsv * dtb = (struct aml_dtb_rsv *) addr;
struct aml_dtb_info *info = &dtb_infos;
u64 blk, cnt, dtb_glb_offset;
int cpy, valid;
struct mmc * mmc;
struct partitions * part = NULL;
struct virtual_partition *vpart = NULL;
vpart = aml_get_virtual_partition_by_name(MMC_DTB_NAME);
part = aml_get_partition_by_name(MMC_RESERVED_NAME);
dtb_glb_offset = part->offset + vpart->offset;
mmc = _dtb_init();
if (NULL == mmc)
return -10;
/* stamp */
valid = info->valid[0] + info->valid[1];
dtb_info("valid %d\n", valid);
if (0 == valid)
dtb->timestamp = 0;
else if (1 == valid) {
dtb->timestamp = 1 + info->stamp[info->valid[0]?0:1];
} else {
/* both are valid */
if (info->stamp[0] != info->stamp[1]) {
dtb_wrn("timestamp are not same %d:%d\n",
info->stamp[0], info->stamp[1]);
dtb->timestamp = 1 + stamp_after(info->stamp[1], info->stamp[0])?
info->stamp[1]:info->stamp[0];
} else
dtb->timestamp = 1 + info->stamp[0];
}
/*setting version and magic*/
dtb->version = 1; /* base version */
dtb->magic = 0x00447e41; /*A~D\0*/
dtb->checksum = _calc_dtb_checksum(dtb);
dtb_info("new stamp %d, checksum 0x%x, version %d, magic %s\n",
dtb->timestamp, dtb->checksum, dtb->version, (char *)&dtb->magic);
for (cpy = 0; cpy < DTB_COPIES; cpy++) {
blk = (dtb_glb_offset + cpy * (vpart->size)) / mmc->read_bl_len;
cnt = vpart->size / mmc->read_bl_len;
ret |= _dtb_write(mmc, blk, cnt, addr);
info->valid[cpy] = 1;
info->stamp[cpy] = dtb->timestamp;
}
return ret;
}
extern int check_valid_dts(unsigned char *buffer);
int renew_partition_tbl(unsigned char *buffer)
{
int ret = 0;
/* todo, check new dts imcoming.... */
ret = check_valid_dts(buffer);
/* only the dts new is valid */
if (!ret) {
free_partitions();
get_partition_from_dts(buffer);
if (0 == mmc_device_init(_dtb_init())) {
printf("partition table success\n");
ret = 0;
goto _out;
}
printf("partition table error\n");
ret = 1;
}
_out:
return ret;
}
int do_amlmmc_dtb_key(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int dev, ret = 0;
void *addr = NULL;
u64 cnt = 0, n = 0, blk = 0;
//u64 size;
struct partitions *part = NULL;
struct virtual_partition *vpart = NULL;
vpart = aml_get_virtual_partition_by_name(MMC_DTB_NAME);
part = aml_get_partition_by_name(MMC_RESERVED_NAME);
switch (argc) {
case 3:
if (strcmp(argv[1], "erase") == 0) {
if (strcmp(argv[2], "dtb") == 0) {
printf("start erase dtb......\n");
dev = EMMC_DTB_DEV;
struct mmc *mmc = find_mmc_device(dev);
if (!mmc) {
printf("not find mmc\n");
return 1;
}
blk = (part->offset + vpart->offset) / mmc->read_bl_len;
cnt = (vpart->size * 2) / mmc->read_bl_len;
if (cnt != 0)
n = mmc->block_dev.block_erase(dev, blk, cnt);
printf("dev # %d, %s, several blocks erased %s\n",
dev, (flag == 0) ? " ":(argv[2]),(n == 0) ? "OK" : "ERROR");
return (n == 0) ? 0 : 1;
}else if (strcmp(argv[2], "key") == 0){
printf("start erase key......\n");
dev = 1;
struct mmc *mmc = find_mmc_device(dev);
if (!mmc) {
printf("not find mmc\n");
return 1;
}
n = mmc_key_erase();
printf("dev # %d, %s, several blocks erased %s\n",
dev, (flag == 0) ? " ":(argv[2]),(n == 0) ? "OK" : "ERROR");
return (n == 0) ? 0 : 1;
}
} else if (strcmp(argv[1], "cali_pattern") == 0) {
if (strcmp(argv[2], "write") == 0) {
dev = EMMC_DTB_DEV;
struct mmc *mmc = find_mmc_device(dev);
if (!mmc) {
printf("not find mmc\n");
return 1;
}
addr = (void *)malloc(CALI_PATTERN_SIZE);
mmc_write_cali_mattern(addr);
vpart = aml_get_virtual_partition_by_name(MMC_PATTERN_NAME);
part = aml_get_partition_by_name(MMC_RESERVED_NAME);
blk = (part->offset + vpart->offset) / mmc->read_bl_len;
cnt = vpart->size / mmc->read_bl_len;
if (cnt != 0)
n = mmc->block_dev.block_write(dev, blk, cnt, addr);
printf("dev # %d, %s, several calibration pattern blocks write %s\n",
dev, (flag == 0) ? " ":(argv[2]),(n == cnt) ? "OK" : "ERROR");
free(addr);
return (n == cnt) ? 0 : 1;
}
}
case 4:
addr = (void *)simple_strtoul(argv[2], NULL, 16);
if (strcmp(argv[1], "dtb_read") == 0) {
/* fixme, */
ret = dtb_read(addr);
return 0;
} else if (strcmp(argv[1], "dtb_write") == 0) {
/* fixme, should we check the return value? */
ret = dtb_write(addr);
ret |= renew_partition_tbl(addr);
return ret;
}
return 0;
default:
break;
}
return 1;
}
int emmc_update_mbr(unsigned char *buffer)
{
int ret = 0;
cpu_id_t cpu_id = get_cpu_id();
if (cpu_id.family_id < MESON_CPU_MAJOR_ID_GXL) {
ret = -1;
printf("MBR not support, try dtb\n");
goto _out;
}
#ifndef DTB_BIND_KERNEL
dtb_write(buffer);
#endif
ret = get_partition_from_dts(buffer);
if (ret) {
printf("Fail to get partition talbe from dts\n");
goto _out;
}
ret = mmc_device_init(_dtb_init());
printf("%s: update mbr %s\n", __func__, ret?"Fail":"Success");
_out:
return ret;
}
U_BOOT_CMD(
emmc, 4, 1, do_amlmmc_dtb_key,
"EMMC sub system",
"emmc dtb_read addr size\n"
"emmc dtb_write addr size\n"
"emmc erase dtb\n"
"emmc erase key\n");