--- /dev/null
+/*
+ * cmd_tizendown.c -- Tizen Downloader
+ *
+ * Copyright (C) 2020 Marek Szyprowski <m.szyprowski@samsung.com>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <malloc.h>
+#include <bootstage.h>
+#include <common.h>
+#include <command.h>
+#include <errno.h>
+#include <memalign.h>
+#include <net.h>
+#include <usb.h>
+#include <dfu.h>
+#include <fs.h>
+
+#ifdef CONFIG_SYS_SDRAM_BASE
+#define DOWNLOAD_ADDR (CONFIG_SYS_SDRAM_BASE + SZ_64K)
+/* 64K shift to avoid 0 (NULL) DOWNLOAD_ADDR on RPi3/4 */
+#else
+#define DOWNLOAD_ADDR 0x40000000
+#endif
+
+#define TRANSFER_SIZE SZ_128M
+
+#define LEN_BUF 128
+#define LEN_NAME 32
+
+#define HASHES_PER_LINE 64 /* Number of "loading" hashes per line */
+
+#if defined(CONFIG_TARGET_RPI_3) || \
+ defined(CONFIG_TARGET_RPI_4) || \
+ defined(CONFIG_TARGET_RPI_3_32B) || \
+ defined(CONFIG_TARGET_RPI_4_32B)
+static char *g_update_image_names[] = {
+ "boot.img",
+ "rootfs.img",
+ "system-data.img",
+ "user.img",
+ "modules.img",
+ "ramdisk.img",
+ "ramdisk-recovery.img"
+};
+#elif defined(CONFIG_TARGET_ODROID_XU3) || defined(CONFIG_TARGET_TIZEN)
+static char *g_update_image_names[] = {
+ "u-boot-mmc.bin",
+ "params.bin",
+ "boot.img",
+ "rootfs.img",
+ "system-data.img",
+ "user.img",
+ "modules.img",
+ "ramdisk.img"
+};
+#else
+#error "Tizen Download is not supported for this board"
+#endif
+
+#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_HOST_ETHER) && !defined(CONFIG_DM_ETH)
+extern char usb_started;
+
+static void do_usb_ether_start(void)
+{
+ bootstage_mark_name(BOOTSTAGE_ID_USB_START, "usb_start");
+ if (usb_init() < 0)
+ return;
+
+ bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start");
+ usb_host_eth_scan(1);
+}
+#endif
+
+static int do_nfs_to_dfu(char *file, char *name)
+{
+ int cnt = 0, alt = dfu_get_alt(name);
+ struct dfu_entity *de;
+ uint done = 0, wdone = 0;
+ int ret;
+ void *buf = (void *)DOWNLOAD_ADDR;
+ int buf_size = TRANSFER_SIZE;
+
+ if (alt < 0) {
+ printf("DFU entity for file %s not found\n", name);
+ return -1;
+ }
+ de = dfu_get_entity(alt);
+
+ do {
+ printf("Downloading %s, offset %u...\n", file, done);
+ ret = netboot_nfs((unsigned long)buf, file, done, buf_size);
+ if (ret != CMD_RET_SUCCESS) {
+ printf("NFS download of file %s failed!!\n", file);
+ return -1;
+ }
+
+ puts("Writing: ");
+ wdone = 0;
+ do {
+ int write_size = min_t(int, dfu_get_buf_size(),
+ net_boot_file_size);
+
+ ret = dfu_write(de, buf + wdone, write_size, cnt++);
+ if (ret)
+ goto done;
+ wdone += write_size;
+ if (cnt % HASHES_PER_LINE == 0)
+ puts("\n\t ");
+ } while (wdone < net_boot_file_size);
+ puts("\n");
+
+ done += net_boot_file_size;
+ } while (net_boot_file_size == buf_size);
+
+ ret = dfu_flush(de, buf, 0, cnt);
+ puts("");
+done:
+ if (ret) {
+ printk("DFU entity write failed\n");
+ return -1;
+ }
+ return (int)done;
+}
+
+static int do_usb_to_dfu(char *file, char *name)
+{
+ int cnt = 0, alt = dfu_get_alt(name);
+ int chunk = 0, chunk_count;
+ struct dfu_entity *de;
+ uint done = 0, wdone = 0;
+ int ret;
+ void *buf = (void *)DOWNLOAD_ADDR;
+ int buf_size = TRANSFER_SIZE;
+ loff_t size = 0;
+ char *fsdev;
+
+ if (alt < 0) {
+ printf("DFU entity for file %s not found\n", name);
+ return -1;
+ }
+
+ fsdev = "0:1";
+ if (fs_set_blk_dev("usb", fsdev, FS_TYPE_ANY)) {
+ fsdev = "0:0";
+ if (fs_set_blk_dev("usb", fsdev, FS_TYPE_ANY)) {
+ printf("Unsupported USB block device..\n");
+ return -1;
+ }
+ }
+
+ ret = fs_size(file, &size);
+ if (ret < 0) {
+ printf("File usb%s%s not found\n", fsdev, file);
+ return -1;
+ }
+
+ chunk_count = 1 + size / buf_size;
+ de = dfu_get_entity(alt);
+
+ do {
+ printf("Reading %s, chunk %d/%d (offset %u) ...\n", file,
+ ++chunk, chunk_count, done);
+
+ if (fs_set_blk_dev("usb", fsdev, FS_TYPE_ANY)) {
+ printf("%d: Unknown block device..\n", __LINE__);
+ return -1;
+ }
+
+ ret = fs_read(file, (unsigned long)buf, done, buf_size, &size);
+ if (ret < 0) {
+ printf("Failed to read %s file %lld.\n", file, size);
+ return -1;
+ }
+
+ puts("Writing: ");
+ wdone = 0;
+ do {
+ int write_size = min_t(int, dfu_get_buf_size(),
+ size);
+ ret = dfu_write(de, buf + wdone, write_size, cnt++);
+ if (ret)
+ goto done;
+ wdone += write_size;
+ if (cnt % HASHES_PER_LINE == 0)
+ puts("\n\t ");
+ } while (wdone < size);
+ puts("\n");
+
+ done += size;
+ } while (size == buf_size);
+
+ ret = dfu_flush(de, buf, 0, cnt);
+ puts("\n");
+done:
+ if (ret) {
+ printk("DFU entity write failed\n");
+ return -1;
+ }
+ return (int)done;
+}
+
+
+static int do_tizen_down(char *path, char *file, int (*func)(char *, char *))
+{
+ char src_path[LEN_BUF];
+ int i, ret = 0;
+ char **images;
+ int image_count;
+ int done_count = 0;
+ int done_bytes[ARRAY_SIZE(g_update_image_names)];
+ char *done_images[ARRAY_SIZE(g_update_image_names)];
+
+ if (file) {
+ images = &file;
+ image_count = 1;
+ } else {
+ images = g_update_image_names;
+ image_count = ARRAY_SIZE(g_update_image_names);
+ }
+
+#if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_HOST_ETHER) && !defined(CONFIG_DM_ETH)
+ if (!usb_started)
+ do_usb_ether_start();
+#endif
+
+ ret = dfu_init_env_entities("mmc", "0");
+ if (ret) {
+ printf("DFU init entities failed\n");
+ return ret;
+ }
+
+ for (i = 0; i < image_count; i++) {
+ char *file_name = images[i];
+ int size;
+
+ snprintf(src_path, LEN_BUF, "%s/%s", path, file_name);
+ size = func(src_path, file_name);
+
+ if (size > 0) {
+ done_images[done_count] = file_name;
+ done_bytes[done_count] = size;
+ done_count++;
+ } else {
+ printf("SKIP %s\n", src_path);
+ }
+ }
+
+ dfu_free_entities();
+
+ puts("########################################\n");
+ for (i = 0; i < done_count; i++)
+ printf("%20s -\t%10d Bytes\n", done_images[i], done_bytes[i]);
+ puts("########################################\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+int do_nfs_down(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ puts("TIZEN \"NFS\" Downloader\n");
+
+ switch (argc) {
+ case 2:
+ return do_tizen_down(argv[1], NULL, do_nfs_to_dfu);
+ case 3:
+ return do_tizen_down(argv[1], argv[2], do_nfs_to_dfu);
+ default:
+ return CMD_RET_USAGE;
+ }
+}
+
+U_BOOT_CMD(nfsdown, CONFIG_SYS_MAXARGS, 1, do_nfs_down,
+ "TIZEN \"NFS\" downloader",
+ "nfsdown [<IP_ADDR>:]<IMG_PATH> [image file name]\n"
+ " - device firmware upgrade via nfs for Tizen\n"
+ " fusing images from <IP_ADDR>:<IMG_PATH> to MMC.\n"
+ " - required to set env for ipaddr, serverip, gatewayip,\n"
+ " and netmask.\n"
+ "\n"
+ "(e.g) nfsdown 192.168.0.1:/nfs [rootfs.img]\n"
+);
+
+int do_usb_down(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ puts("TIZEN \"USB\" Downloader\n");
+
+ switch (argc) {
+ case 1:
+ return do_tizen_down("/updateTizen", NULL, do_usb_to_dfu);
+ case 2:
+ return do_tizen_down("/updateTizen", argv[1], do_usb_to_dfu);
+ default:
+ return CMD_RET_USAGE;
+ }
+}
+
+U_BOOT_CMD(usbdown, CONFIG_SYS_MAXARGS, 1, do_usb_down,
+ "TIZEN \"USB\" downloader",
+ "usbdown [image file name]\n"
+ " - device firmware upgrade via USB mass-storage for Tizen\n"
+ " fusing images from \"updateTizen\" USB directory to MMC.\n"
+ " - \"updateTizen\" directory has to be on the first partition\n"
+ " of the USB storage\n"
+ "(e.g) tizendown [rootfs.img]\n\n"
+);