i2S6ULY2: support update eMMC partition
This function reference by Digi U-Boot.
This commit is contained in:
@ -11,3 +11,4 @@ obj-$(CONFIG_CMD_BOOTSTREAM) += cmd_bootstream/bch.o \
|
||||
cmd_bootstream/cmd_bootstream.o \
|
||||
cmd_bootstream/mxsboot.o
|
||||
obj-$(CONFIG_CMD_UPDATE_NAND) += cmd_update_nand.o helper.o
|
||||
obj-$(CONFIG_CMD_UPDATE_MMC) += cmd_update_mmc.o helper.o helper_mmc.o
|
||||
|
||||
753
board/i2som/common/cmd_update_mmc.c
Normal file
753
board/i2som/common/cmd_update_mmc.c
Normal file
@ -0,0 +1,753 @@
|
||||
/*
|
||||
* Copyright (C) 2014 by Digi International Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <asm/imx-common/boot_mode.h>
|
||||
#include <otf_update.h>
|
||||
#include <part.h>
|
||||
#include <mmc.h>
|
||||
#include "helper.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
static block_dev_desc_t *mmc_dev;
|
||||
static int mmc_dev_index;
|
||||
|
||||
static int mmc_get_bootdevindex(void);
|
||||
static int board_update_chunk(otf_data_t *oftd);
|
||||
extern void register_tftp_otf_update_hook(int (*hook)(otf_data_t *oftd),
|
||||
disk_partition_t*);
|
||||
extern void unregister_tftp_otf_update_hook(void);
|
||||
extern void register_fs_otf_update_hook(int (*hook)(otf_data_t *oftd),
|
||||
disk_partition_t*);
|
||||
extern void unregister_fs_otf_update_hook(void);
|
||||
|
||||
int register_otf_hook(int src, int (*hook)(otf_data_t *oftd),
|
||||
disk_partition_t *partition)
|
||||
{
|
||||
switch (src) {
|
||||
case SRC_MMC:
|
||||
case SRC_USB:
|
||||
case SRC_SATA:
|
||||
register_fs_otf_update_hook(hook, partition);
|
||||
return 1;
|
||||
case SRC_NFS:
|
||||
//TODO
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void unregister_otf_hook(int src)
|
||||
{
|
||||
switch (src) {
|
||||
case SRC_MMC:
|
||||
unregister_fs_otf_update_hook();
|
||||
break;
|
||||
case SRC_USB:
|
||||
case SRC_SATA:
|
||||
case SRC_NFS:
|
||||
//TODO
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum {
|
||||
ERR_WRITE = 1,
|
||||
ERR_READ,
|
||||
ERR_VERIFY,
|
||||
};
|
||||
|
||||
static int write_firmware(char *partname, unsigned long loadaddr,
|
||||
unsigned long filesize, disk_partition_t *info)
|
||||
{
|
||||
char cmd[CONFIG_SYS_CBSIZE] = "";
|
||||
unsigned long size_blks, verifyaddr, u, m;
|
||||
|
||||
size_blks = (filesize / mmc_dev->blksz) + (filesize % mmc_dev->blksz != 0);
|
||||
|
||||
if (size_blks > info->size) {
|
||||
printf("File size (%lu bytes) exceeds partition size (%lu bytes)!\n",
|
||||
filesize,
|
||||
info->size * mmc_dev->blksz);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prepare command to change to storage device */
|
||||
sprintf(cmd, "%s dev %d", CONFIG_SYS_STORAGE_MEDIA, mmc_dev_index);
|
||||
|
||||
/*
|
||||
* If updating U-Boot on eMMC
|
||||
* append the hardware partition where U-Boot lives.
|
||||
*/
|
||||
if (!strcmp(partname, "uboot") &&
|
||||
!strcmp(CONFIG_SYS_STORAGE_MEDIA, "mmc") &&
|
||||
(mmc_dev_index == 0))
|
||||
strcat(cmd, " $mmcbootpart");
|
||||
|
||||
/* Change to storage device */
|
||||
if (run_command(cmd, 0)) {
|
||||
debug("Cannot change to storage device\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Write firmware command */
|
||||
sprintf(cmd, "%s write %lx %lx %lx", CONFIG_SYS_STORAGE_MEDIA,
|
||||
loadaddr, info->start, size_blks);
|
||||
if (run_command(cmd, 0))
|
||||
return ERR_WRITE;
|
||||
|
||||
/* If there is enough RAM to hold two copies of the firmware,
|
||||
* verify written firmware.
|
||||
* +--------|---------------------|------------------|--------------+
|
||||
* | L V | U-Boot+Stack |
|
||||
* +--------|---------------------|------------------|--------------+
|
||||
* P U M
|
||||
*
|
||||
* P = PHYS_SDRAM (base address of SDRAM)
|
||||
* L = $loadaddr
|
||||
* V = $verifyaddr
|
||||
* M = last address of SDRAM (CONFIG_DDR_SIZE (size of SDRAM) + P)
|
||||
* U = SDRAM address where U-Boot is located (plus margin)
|
||||
*/
|
||||
verifyaddr = getenv_ulong("verifyaddr", 16, 0);
|
||||
m = PHYS_SDRAM + (CONFIG_DDR_SIZE * 1024LU * 1024LU);
|
||||
u = m - CONFIG_UBOOT_RESERVED;
|
||||
|
||||
/* ($loadaddr + firmware size) must not exceed $verifyaddr
|
||||
* ($verifyaddr + firmware size) must not exceed U
|
||||
*/
|
||||
if ((loadaddr + size_blks * mmc_dev->blksz) < verifyaddr &&
|
||||
(verifyaddr + size_blks * mmc_dev->blksz) < u) {
|
||||
unsigned long filesize_padded;
|
||||
int i;
|
||||
|
||||
/* Read back data... */
|
||||
printf("Reading back firmware...\n");
|
||||
sprintf(cmd, "%s read %lx %lx %lx", CONFIG_SYS_STORAGE_MEDIA,
|
||||
verifyaddr, info->start, size_blks);
|
||||
if (run_command(cmd, 0))
|
||||
return ERR_READ;
|
||||
/* ...then compare by 32-bit words (faster than by bytes)
|
||||
* padding with zeros any bytes at the end to make the size
|
||||
* be a multiple of 4.
|
||||
*
|
||||
* Reference: http://stackoverflow.com/a/2022252
|
||||
*/
|
||||
printf("Verifying firmware...\n");
|
||||
filesize_padded = (filesize + (4 - 1)) & ~(4 - 1);
|
||||
|
||||
for (i = filesize; i < filesize_padded; i++) {
|
||||
*((char *)loadaddr + i) = 0;
|
||||
*((char *)verifyaddr + i) = 0;
|
||||
}
|
||||
sprintf(cmd, "cmp.l %lx %lx %lx", loadaddr, verifyaddr,
|
||||
(filesize_padded / 4));
|
||||
if (run_command(cmd, 0))
|
||||
return ERR_VERIFY;
|
||||
printf("Update was successful\n");
|
||||
} else {
|
||||
printf("Firmware updated but not verified "
|
||||
"(not enough available RAM to verify)\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_file(char *targetfilename, char *targetfs, int part)
|
||||
{
|
||||
char cmd[CONFIG_SYS_CBSIZE] = "";
|
||||
unsigned long loadaddr, filesize;
|
||||
|
||||
loadaddr = getenv_ulong("loadaddr", 16, CONFIG_LOADADDR);
|
||||
filesize = getenv_ulong("filesize", 16, 0);
|
||||
|
||||
/* Change to storage device */
|
||||
sprintf(cmd, "%s dev %d", CONFIG_SYS_STORAGE_MEDIA, mmc_dev_index);
|
||||
if (run_command(cmd, 0)) {
|
||||
debug("Cannot change to storage device\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prepare write command */
|
||||
sprintf(cmd, "%swrite %s %d:%d %lx %s %lx", targetfs,
|
||||
CONFIG_SYS_STORAGE_MEDIA, mmc_dev_index, part,
|
||||
loadaddr, targetfilename, filesize);
|
||||
|
||||
return run_command(cmd, 0);
|
||||
}
|
||||
|
||||
#define ECSD_PARTITION_CONFIG 179
|
||||
#define BOOT_ACK (1 << 6)
|
||||
#define BOOT_PARTITION_ENABLE_OFF 3
|
||||
|
||||
static int emmc_bootselect(void)
|
||||
{
|
||||
char cmd[CONFIG_SYS_CBSIZE] = "";
|
||||
int bootpart;
|
||||
|
||||
/* Prepare command to change to storage device */
|
||||
sprintf(cmd, "mmc dev %d", mmc_dev_index);
|
||||
|
||||
/* Change to storage device */
|
||||
if (run_command(cmd, 0)) {
|
||||
debug("Cannot change to storage device\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Select boot partition and enable boot acknowledge */
|
||||
bootpart = getenv_ulong("mmcbootpart", 16, CONFIG_SYS_BOOT_PART_EMMC);
|
||||
sprintf(cmd, "mmc ecsd write %x %x", ECSD_PARTITION_CONFIG,
|
||||
BOOT_ACK | (bootpart << BOOT_PARTITION_ENABLE_OFF));
|
||||
|
||||
return run_command(cmd, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns the size of available RAM holding a firmware transfer.
|
||||
* This size depends on:
|
||||
* - The total RAM available
|
||||
* - The loadaddr
|
||||
* - The RAM occupied by U-Boot and its location
|
||||
*/
|
||||
static unsigned int get_available_ram_for_update(void)
|
||||
{
|
||||
unsigned int loadaddr;
|
||||
unsigned int la_off;
|
||||
|
||||
loadaddr = getenv_ulong("loadaddr", 16, CONFIG_LOADADDR);
|
||||
la_off = loadaddr - gd->bd->bi_dram[0].start;
|
||||
|
||||
return (gd->bd->bi_dram[0].size - CONFIG_UBOOT_RESERVED - la_off);
|
||||
}
|
||||
|
||||
static int init_mmc_globals(void)
|
||||
{
|
||||
/* Use the device in $mmcdev or else, the boot media */
|
||||
mmc_dev_index = getenv_ulong("mmcdev", 16, mmc_get_bootdevindex());
|
||||
mmc_dev = mmc_get_dev(mmc_dev_index);
|
||||
if (NULL == mmc_dev) {
|
||||
debug("Cannot determine sys storage device\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_update(cmd_tbl_t* cmdtp, int flag, int argc, char * const argv[])
|
||||
{
|
||||
disk_partition_t info;
|
||||
int ret;
|
||||
int otf = 0;
|
||||
int otf_enabled = 0;
|
||||
char cmd[CONFIG_SYS_CBSIZE] = "";
|
||||
unsigned long loadaddr;
|
||||
unsigned long verifyaddr;
|
||||
unsigned long filesize = 0;
|
||||
struct load_fw fwinfo;
|
||||
|
||||
if (argc < 2)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
memset(&fwinfo, 0, sizeof(fwinfo));
|
||||
|
||||
if (init_mmc_globals())
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
/* Get data of partition to be updated */
|
||||
if (!strcmp(argv[1], "uboot")) {
|
||||
/* Simulate partition data for U-Boot */
|
||||
info.start = CONFIG_SYS_BOOT_PART_OFFSET / mmc_dev->blksz;
|
||||
info.size = CONFIG_SYS_BOOT_PART_SIZE / mmc_dev->blksz;
|
||||
strcpy((char *)info.name, argv[1]);
|
||||
} else {
|
||||
/* Not a reserved name. Must be a partition name or index */
|
||||
char dev_index_str[2];
|
||||
|
||||
/* Look up partition on the device */
|
||||
sprintf(dev_index_str, "%d", mmc_dev_index);
|
||||
if (get_partition_bynameorindex(CONFIG_SYS_STORAGE_MEDIA,
|
||||
dev_index_str, argv[1], &info) < 0) {
|
||||
printf("Error: partition '%s' not found\n", argv[1]);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ask for confirmation if needed */
|
||||
if (getenv_yesno("forced_update") != 1) {
|
||||
/* Confirm programming */
|
||||
if (!strcmp((char *)info.name, "uboot") &&
|
||||
!confirm_msg("Do you really want to program "
|
||||
"the boot loader? <y/N> "))
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
/* Get source of update firmware file */
|
||||
if (get_source(argc, argv, &fwinfo))
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
loadaddr = getenv_ulong("loadaddr", 16, CONFIG_LOADADDR);
|
||||
/*
|
||||
* If undefined, calculate 'verifyaddr' as halfway through the RAM
|
||||
* from $loadaddr.
|
||||
*/
|
||||
if (NULL == getenv("verifyaddr")) {
|
||||
verifyaddr = loadaddr +
|
||||
((gd->ram_size - (loadaddr - PHYS_SDRAM)) / 2);
|
||||
if (verifyaddr > loadaddr &&
|
||||
verifyaddr < (PHYS_SDRAM + gd->ram_size))
|
||||
setenv_hex("verifyaddr", verifyaddr);
|
||||
}
|
||||
|
||||
if (fwinfo.src == SRC_RAM) {
|
||||
/* Get address in RAM where firmware file is */
|
||||
if (argc > 3)
|
||||
loadaddr = simple_strtol(argv[3], NULL, 16);
|
||||
|
||||
/* Get filesize */
|
||||
if (argc > 4)
|
||||
filesize = simple_strtol(argv[4], NULL, 16);
|
||||
} else {
|
||||
if (getenv_yesno("otf-update") == -1) {
|
||||
/*
|
||||
* If otf-update is undefined, check if there is enough
|
||||
* RAM to hold the largest possible file that fits into
|
||||
* the destiny partition.
|
||||
*/
|
||||
unsigned long avail = get_available_ram_for_update();
|
||||
|
||||
if (avail <= info.size * mmc_dev->blksz) {
|
||||
printf("Partition to update is larger (%d MiB) than the\n"
|
||||
"available RAM memory (%d MiB, starting at $loadaddr=0x%08x).\n",
|
||||
(int)(info.size * mmc_dev->blksz / (1024 * 1024)),
|
||||
(int)(avail / (1024 * 1024)),
|
||||
(unsigned int)loadaddr);
|
||||
printf("Activating On-the-fly update mechanism.\n");
|
||||
otf_enabled = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get firmware file name */
|
||||
ret = get_fw_filename(argc, argv, &fwinfo);
|
||||
if (ret) {
|
||||
/* Filename was not provided. Look for default one */
|
||||
fwinfo.filename = get_default_filename(argv[1],
|
||||
CMD_UPDATE);
|
||||
if (!fwinfo.filename) {
|
||||
printf("Error: need a filename\n");
|
||||
return CMD_RET_USAGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Activate on-the-fly update if needed */
|
||||
if (otf_enabled || (getenv_yesno("otf-update") == 1)) {
|
||||
if (!strcmp((char *)info.name, "uboot")) {
|
||||
/* Do not activate on-the-fly update for U-Boot */
|
||||
printf("On-the-fly mechanism disabled for U-Boot "
|
||||
"for security reasons\n");
|
||||
} else {
|
||||
/* register on-the-fly update mechanism */
|
||||
otf = register_otf_hook(fwinfo.src, board_update_chunk,
|
||||
&info);
|
||||
}
|
||||
}
|
||||
|
||||
if (otf) {
|
||||
/* Prepare command to change to storage device */
|
||||
sprintf(cmd, CONFIG_SYS_STORAGE_MEDIA " dev %d", mmc_dev_index);
|
||||
/* Change to storage device */
|
||||
if (run_command(cmd, 0)) {
|
||||
printf("Error: cannot change to storage device\n");
|
||||
ret = CMD_RET_FAILURE;
|
||||
goto _ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (fwinfo.src != SRC_RAM) {
|
||||
/*
|
||||
* Load firmware file to RAM (this process may write the file
|
||||
* to the target media if OTF mechanism is enabled).
|
||||
*/
|
||||
fwinfo.loadaddr = "$loadaddr";
|
||||
ret = load_firmware(&fwinfo);
|
||||
if (ret == LDFW_ERROR) {
|
||||
printf("Error loading firmware file to RAM\n");
|
||||
ret = CMD_RET_FAILURE;
|
||||
goto _ret;
|
||||
} else if (ret == LDFW_LOADED && otf) {
|
||||
ret = CMD_RET_SUCCESS;
|
||||
goto _ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Write firmware file from RAM to storage */
|
||||
if (!filesize)
|
||||
filesize = getenv_ulong("filesize", 16, 0);
|
||||
ret = write_firmware(argv[1], loadaddr, filesize, &info);
|
||||
if (ret) {
|
||||
if (ret == ERR_READ)
|
||||
printf("Error while reading back written firmware!\n");
|
||||
else if (ret == ERR_VERIFY)
|
||||
printf("Error while verifying written firmware!\n");
|
||||
else
|
||||
printf("Error writing firmware!\n");
|
||||
ret = CMD_RET_FAILURE;
|
||||
goto _ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* If updating U-Boot into eMMC, instruct the eMMC to boot from
|
||||
* special hardware partition.
|
||||
*/
|
||||
if (!strcmp(argv[1], "uboot") &&
|
||||
!strcmp(CONFIG_SYS_STORAGE_MEDIA, "mmc") &&
|
||||
(mmc_dev_index == 0)) {
|
||||
ret = emmc_bootselect();
|
||||
if (ret) {
|
||||
printf("Error changing eMMC boot partition\n");
|
||||
ret = CMD_RET_FAILURE;
|
||||
goto _ret;
|
||||
}
|
||||
}
|
||||
|
||||
_ret:
|
||||
unregister_otf_hook(fwinfo.src);
|
||||
return ret;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(
|
||||
update, 6, 0, do_update,
|
||||
"i2SOM modules update command",
|
||||
"<partition> [source] [extra-args...]\n"
|
||||
" Description: updates (raw writes) <partition> in $mmcdev via <source>\n"
|
||||
" Arguments:\n"
|
||||
" - partition: a partition index, a GUID partition name, or one\n"
|
||||
" of the reserved names: uboot\n"
|
||||
" - [source]: " CONFIG_UPDATE_SUPPORTED_SOURCES_LIST "\n"
|
||||
" - [extra-args]: extra arguments depending on 'source'\n"
|
||||
"\n"
|
||||
CONFIG_UPDATE_SUPPORTED_SOURCES_ARGS_HELP
|
||||
);
|
||||
|
||||
/* Certain command line arguments of 'update' command may be at different
|
||||
* index depending on the selected <source>. This function returns in 'arg'
|
||||
* the argument at <index> plus an offset that depends on the selected <source>
|
||||
* Upon calling, the <index> must be given as if <source> was SRC_RAM.
|
||||
*/
|
||||
static int get_arg_src(int argc, char * const argv[], int src, int index,
|
||||
char **arg)
|
||||
{
|
||||
switch (src) {
|
||||
case SRC_TFTP:
|
||||
case SRC_NFS:
|
||||
index += 1;
|
||||
break;
|
||||
case SRC_MMC:
|
||||
case SRC_USB:
|
||||
case SRC_SATA:
|
||||
index += 3;
|
||||
break;
|
||||
case SRC_RAM:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc > index) {
|
||||
*arg = (char *)argv[index];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mmc_get_bootdevindex(void)
|
||||
{
|
||||
switch(get_boot_device()) {
|
||||
case SD1_BOOT ... SD4_BOOT:
|
||||
return 1;
|
||||
#if 0
|
||||
/* SD card */
|
||||
if (board_has_emmc())
|
||||
return 1; /* index of USDHC2 if SOM has eMMC */
|
||||
else
|
||||
return 0; /* index of USDHC2 if SOM has no eMMC */
|
||||
#endif
|
||||
case MMC4_BOOT:
|
||||
return 0; /* index of SDHC4 (eMMC) */
|
||||
default:
|
||||
/* return default value otherwise */
|
||||
return CONFIG_SYS_MMC_ENV_DEV;
|
||||
}
|
||||
}
|
||||
|
||||
static int write_chunk(struct mmc *mmc, otf_data_t *otfd, unsigned int dstblk,
|
||||
unsigned int chunklen)
|
||||
{
|
||||
int sectors;
|
||||
unsigned long written, read, verifyaddr;
|
||||
|
||||
printf("\nWriting chunk...");
|
||||
/* Check WP */
|
||||
if (mmc_getwp(mmc) == 1) {
|
||||
printf("[Error]: card is write protected!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We can only write whole sectors (multiples of mmc_dev->blksz bytes)
|
||||
* so we need to check if the chunk is a whole multiple or else add 1
|
||||
*/
|
||||
sectors = chunklen / mmc_dev->blksz;
|
||||
if (chunklen % mmc_dev->blksz)
|
||||
sectors++;
|
||||
|
||||
/* Check if chunk fits */
|
||||
if (sectors + dstblk > otfd->part->start + otfd->part->size) {
|
||||
printf("[Error]: length of data exceeds partition size\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Write chunklen bytes of chunk to media */
|
||||
debug("writing chunk of 0x%x bytes (0x%x sectors) "
|
||||
"from 0x%x to block 0x%x\n",
|
||||
chunklen, sectors, otfd->loadaddr, dstblk);
|
||||
written = mmc->block_dev.block_write(mmc_dev_index, dstblk, sectors,
|
||||
(const void *)otfd->loadaddr);
|
||||
if (written != sectors) {
|
||||
printf("[Error]: written sectors != sectors to write\n");
|
||||
return -1;
|
||||
}
|
||||
printf("[OK]\n");
|
||||
|
||||
/* Verify written chunk if $loadaddr + chunk size does not overlap
|
||||
* $verifyaddr (where the read-back copy will be placed)
|
||||
*/
|
||||
verifyaddr = getenv_ulong("verifyaddr", 16, 0);
|
||||
if (otfd->loadaddr + sectors * mmc_dev->blksz < verifyaddr) {
|
||||
/* Read back data... */
|
||||
printf("Reading back chunk...");
|
||||
read = mmc->block_dev.block_read(mmc_dev_index, dstblk, sectors,
|
||||
(void *)verifyaddr);
|
||||
if (read != sectors) {
|
||||
printf("[Error]: read sectors != sectors to read\n");
|
||||
return -1;
|
||||
}
|
||||
printf("[OK]\n");
|
||||
/* ...then compare */
|
||||
printf("Verifying chunk...");
|
||||
if (memcmp((const void *)otfd->loadaddr,
|
||||
(const void *)verifyaddr,
|
||||
sectors * mmc_dev->blksz)) {
|
||||
printf("[Error]\n");
|
||||
return -1;
|
||||
} else {
|
||||
printf("[OK]\n");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
printf("[Warning]: Cannot verify chunk. "
|
||||
"It overlaps $verifyaddr!\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* writes a chunk of data from RAM to main storage media (eMMC) */
|
||||
int board_update_chunk(otf_data_t *otfd)
|
||||
{
|
||||
static unsigned int chunk_len = 0;
|
||||
static unsigned int dstblk = 0;
|
||||
struct mmc *mmc;
|
||||
|
||||
if (mmc_dev_index == -1)
|
||||
mmc_dev_index = getenv_ulong("mmcdev", 16,
|
||||
mmc_get_bootdevindex());
|
||||
mmc = find_mmc_device(mmc_dev_index);
|
||||
if (NULL == mmc)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* There are two variants:
|
||||
* - otfd.buf == NULL
|
||||
* In this case, the data is already waiting on the correct
|
||||
* address in RAM, waiting to be written to the media.
|
||||
* - otfd.buf != NULL
|
||||
* In this case, the data is on the buffer and must still be
|
||||
* copied to an address in RAM, before it is written to media.
|
||||
*/
|
||||
if (otfd->buf && otfd->len) {
|
||||
/*
|
||||
* If data is in the otfd->buf buffer, copy it to the loadaddr
|
||||
* in RAM until we have a chunk that is at least as large as
|
||||
* CONFIG_OTF_CHUNK, to write it to media.
|
||||
*/
|
||||
memcpy((void *)(otfd->loadaddr + otfd->offset), otfd->buf,
|
||||
otfd->len);
|
||||
}
|
||||
|
||||
/* Initialize dstblk and local variables */
|
||||
if (otfd->flags & OTF_FLAG_INIT) {
|
||||
chunk_len = 0;
|
||||
dstblk = otfd->part->start;
|
||||
otfd->flags &= ~OTF_FLAG_INIT;
|
||||
}
|
||||
chunk_len += otfd->len;
|
||||
|
||||
/* The flush flag is set when the download process has finished
|
||||
* meaning we must write the remaining bytes in RAM to the storage
|
||||
* media. After this, we must quit the function. */
|
||||
if (otfd->flags & OTF_FLAG_FLUSH) {
|
||||
/* Write chunk with remaining bytes */
|
||||
if (chunk_len) {
|
||||
if (write_chunk(mmc, otfd, dstblk, chunk_len))
|
||||
return -1;
|
||||
}
|
||||
/* Reset all static variables if offset == 0 (starting chunk) */
|
||||
chunk_len = 0;
|
||||
dstblk = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (chunk_len >= CONFIG_OTF_CHUNK) {
|
||||
unsigned int remaining;
|
||||
/* We have CONFIG_OTF_CHUNK (or more) bytes in RAM.
|
||||
* Let's proceed to write as many as multiples of blksz
|
||||
* as possible.
|
||||
*/
|
||||
remaining = chunk_len % mmc_dev->blksz;
|
||||
chunk_len -= remaining; /* chunk_len is now multiple of blksz */
|
||||
|
||||
if (write_chunk(mmc, otfd, dstblk, chunk_len))
|
||||
return -1;
|
||||
|
||||
/* increment destiny block */
|
||||
dstblk += (chunk_len / mmc_dev->blksz);
|
||||
/* copy excess of bytes from previous chunk to offset 0 */
|
||||
if (remaining) {
|
||||
memcpy((void *)otfd->loadaddr,
|
||||
(void *)(otfd->loadaddr + chunk_len),
|
||||
remaining);
|
||||
debug("Copying excess of %d bytes to offset 0\n",
|
||||
remaining);
|
||||
}
|
||||
/* reset chunk_len to excess of bytes from previous chunk
|
||||
* (or zero, if that's the case) */
|
||||
chunk_len = remaining;
|
||||
}
|
||||
/* Set otfd offset pointer to offset in RAM where new bytes would
|
||||
* be written. This offset may be reused by caller */
|
||||
otfd->offset = chunk_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#if 0
|
||||
static int do_updatefile(cmd_tbl_t* cmdtp, int flag, int argc,
|
||||
char * const argv[])
|
||||
{
|
||||
disk_partition_t info;
|
||||
char *targetfilename = NULL;
|
||||
char *targetfs = NULL;
|
||||
const char *default_fs = "fat";
|
||||
int part;
|
||||
int i;
|
||||
char *supported_fs[] = {
|
||||
#ifdef CONFIG_FAT_WRITE
|
||||
"fat",
|
||||
#endif
|
||||
#ifdef CONFIG_EXT4_WRITE
|
||||
"ext4",
|
||||
#endif
|
||||
};
|
||||
char dev_index_str[2];
|
||||
struct load_fw fwinfo;
|
||||
|
||||
if (argc < 2)
|
||||
return CMD_RET_USAGE;
|
||||
|
||||
memset(&fwinfo, 0, sizeof(fwinfo));
|
||||
|
||||
if (init_mmc_globals())
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
/* Get data of partition to be updated */
|
||||
sprintf(dev_index_str, "%d", mmc_dev_index);
|
||||
part = get_partition_bynameorindex(CONFIG_SYS_STORAGE_MEDIA,
|
||||
dev_index_str, argv[1], &info);
|
||||
if (part < 0) {
|
||||
printf("Error: partition '%s' not found\n", argv[1]);
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
/* Get source of update firmware file */
|
||||
if (get_source(argc, argv, &fwinfo))
|
||||
return CMD_RET_FAILURE;
|
||||
|
||||
/* Get file name */
|
||||
if (get_arg_src(argc, argv, fwinfo.src, 2, &fwinfo.filename)) {
|
||||
printf("Error: need a filename\n");
|
||||
return CMD_RET_USAGE;
|
||||
}
|
||||
|
||||
/* Get target file name. If not provided use fwinfo.filename by default */
|
||||
if (get_arg_src(argc, argv, fwinfo.src, 3, &targetfilename))
|
||||
targetfilename = fwinfo.filename;
|
||||
|
||||
/* Get target filesystem. If not provided use 'fat' by default */
|
||||
if (get_arg_src(argc, argv, fwinfo.src, 4, &targetfs))
|
||||
targetfs = (char *)default_fs;
|
||||
|
||||
/* Check target fs is supported */
|
||||
for (i = 0; i < ARRAY_SIZE(supported_fs); i++)
|
||||
if (!strcmp(targetfs, supported_fs[i]))
|
||||
break;
|
||||
|
||||
if (i >= ARRAY_SIZE(supported_fs)) {
|
||||
printf("Error: target file system '%s' is unsupported for "
|
||||
"write operation.\n"
|
||||
"Valid file systems are: ", targetfs);
|
||||
for (i = 0; i < ARRAY_SIZE(supported_fs); i++)
|
||||
printf("%s ", supported_fs[i]);
|
||||
printf("\n");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
/* Load firmware file to RAM */
|
||||
fwinfo.loadaddr = "$loadaddr";
|
||||
if (LDFW_ERROR == load_firmware(&fwinfo)) {
|
||||
printf("Error loading firmware file to RAM\n");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
/* Write file from RAM to storage partition */
|
||||
if (write_file(targetfilename, targetfs, part)) {
|
||||
printf("Error writing file\n");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
return CMD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
U_BOOT_CMD(
|
||||
updatefile, 8, 0, do_updatefile,
|
||||
"i2SOM modules updatefile command",
|
||||
"<partition> [source] [extra-args...]\n"
|
||||
" Description: updates/writes a file in <partition> in $mmcdev via\n"
|
||||
" <source>\n"
|
||||
" Arguments:\n"
|
||||
" - partition: a partition index or a GUID partition name where\n"
|
||||
" to upload the file\n"
|
||||
" - [source]: " CONFIG_UPDATE_SUPPORTED_SOURCES_LIST "\n"
|
||||
" - [extra-args]: extra arguments depending on 'source'\n"
|
||||
"\n"
|
||||
CONFIG_UPDATEFILE_SUPPORTED_SOURCES_ARGS_HELP
|
||||
);
|
||||
#endif
|
||||
173
board/i2som/common/helper_mmc.c
Normal file
173
board/i2som/common/helper_mmc.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2017 by Digi International Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <mmc.h>
|
||||
#include <environment.h>
|
||||
#include <malloc.h>
|
||||
|
||||
extern int mmc_get_bootdevindex(void);
|
||||
|
||||
/*
|
||||
* Common function to get the block size of the storage media.
|
||||
*/
|
||||
size_t media_get_block_size(void)
|
||||
{
|
||||
size_t block_size = 0;
|
||||
struct mmc *mmc;
|
||||
|
||||
mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
|
||||
if (mmc)
|
||||
block_size = mmc->block_dev.blksz;
|
||||
return block_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the offset of a partition in the mmc
|
||||
* @in: Partition name
|
||||
* @in: Offset of the partition in mmc block units
|
||||
* return 0 if success
|
||||
*/
|
||||
int get_partition_offset(char *part_name, u32 *offset)
|
||||
{
|
||||
disk_partition_t info;
|
||||
char dev_index_str[2];
|
||||
int r;
|
||||
|
||||
sprintf(dev_index_str, "%d", mmc_get_bootdevindex());
|
||||
r = get_partition_bynameorindex(CONFIG_SYS_STORAGE_MEDIA,
|
||||
dev_index_str,
|
||||
part_name,
|
||||
&info);
|
||||
if (r < 0)
|
||||
return r;
|
||||
*offset = info.start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a data block from the storage media.
|
||||
* This function only reads one mmc block
|
||||
* @in: Address in media (must be aligned to block size)
|
||||
* @in: Read buffer
|
||||
* @in: Partition index, only applies for MMC
|
||||
* The function returns:
|
||||
* 0 if success and data in readbuf.
|
||||
* -1 on error
|
||||
*/
|
||||
int media_read_block(u32 addr, unsigned char *readbuf, uint hwpart)
|
||||
{
|
||||
size_t len;
|
||||
size_t nbytes;
|
||||
int ret = -1;
|
||||
struct mmc *mmc;
|
||||
uint orig_part;
|
||||
u32 loadaddr = getenv_ulong("loadaddr", 16, load_addr);
|
||||
|
||||
len = media_get_block_size();
|
||||
if (len <= 0)
|
||||
return ret;
|
||||
|
||||
mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
|
||||
if (!mmc)
|
||||
return ret;
|
||||
|
||||
//orig_part = mmc->part_num;
|
||||
if (mmc_switch_part(CONFIG_SYS_MMC_ENV_DEV, hwpart))
|
||||
return ret;
|
||||
|
||||
nbytes = mmc->block_dev.block_read(CONFIG_SYS_MMC_ENV_DEV,
|
||||
addr,
|
||||
1,
|
||||
(void *)loadaddr);
|
||||
if (nbytes == 1) {
|
||||
memcpy(readbuf, (const void *)loadaddr, len);
|
||||
ret = 0;
|
||||
}
|
||||
//mmc_switch_part(CONFIG_SYS_MMC_ENV_DEV, orig_part);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a data block to the storage media.
|
||||
* This function only writes the minimum required amount of data:
|
||||
* for mmc: one block (probably 512 bytes)
|
||||
* @in: Address in media (must be aligned to block size)
|
||||
* @in: Write buffer
|
||||
* @in: Partition index, only applies for MMC
|
||||
* The function returns:
|
||||
* 0 if success
|
||||
* -1 on error
|
||||
*/
|
||||
int media_write_block(u32 addr, unsigned char *writebuf, uint hwpart)
|
||||
{
|
||||
size_t len;
|
||||
int ret = -1;
|
||||
struct mmc *mmc;
|
||||
unsigned long written;
|
||||
uint orig_part;
|
||||
|
||||
len = media_get_block_size();
|
||||
if (len <= 0)
|
||||
return ret;
|
||||
|
||||
mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
|
||||
if (!mmc)
|
||||
return ret;
|
||||
|
||||
//orig_part = mmc->part_num;
|
||||
if (mmc_switch_part(CONFIG_SYS_MMC_ENV_DEV, hwpart))
|
||||
return ret;
|
||||
written = mmc->block_dev.block_write(CONFIG_SYS_MMC_ENV_DEV,
|
||||
addr,
|
||||
1,
|
||||
writebuf);
|
||||
if (written == 1)
|
||||
ret = 0;
|
||||
//mmc_switch_part(CONFIG_SYS_MMC_ENV_DEV, orig_part);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Erase data in the storage media.
|
||||
* This function only erases the minimum required amount of data:
|
||||
* for mmc: one block (probably 512 bytes)
|
||||
* @in: Address in media (must be aligned to block size)
|
||||
* @in: Partition index, only applies for MMC
|
||||
*/
|
||||
void media_erase_fskey(u32 addr, uint hwpart)
|
||||
{
|
||||
struct mmc *mmc;
|
||||
unsigned char *zero_buf;
|
||||
uint orig_part = 0;
|
||||
|
||||
zero_buf = calloc(1, media_get_block_size());
|
||||
if (!zero_buf)
|
||||
return;
|
||||
|
||||
mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
|
||||
if (!mmc)
|
||||
return;
|
||||
//orig_part = mmc->part_num;
|
||||
if (mmc_switch_part(CONFIG_SYS_MMC_ENV_DEV, hwpart))
|
||||
return;
|
||||
/*
|
||||
* Do not use block_erase, it uses different erase ranges
|
||||
* and will erase the full environment.
|
||||
*/
|
||||
media_write_block(addr, zero_buf, mmc_get_env_part(mmc));
|
||||
//mmc_switch_part(CONFIG_SYS_MMC_ENV_DEV, orig_part);
|
||||
}
|
||||
|
||||
uint get_env_hwpart(void)
|
||||
{
|
||||
struct mmc *mmc;
|
||||
|
||||
mmc = find_mmc_device(mmc_get_bootdevindex());
|
||||
return mmc_get_env_part(mmc);
|
||||
}
|
||||
Reference in New Issue
Block a user