/* * Copyright (c) 2018, 2020 The Linux Foundation. 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 version 2 and * only version 2 as published by the Free Software Foundation. * 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. */ /* * FlashWrite command support */ #include #include #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; #ifndef CONFIG_SDHCI_SUPPORT extern qca_mmc mmc_host; #else extern struct sdhci_host mmc_host; #endif #define SMEM_PTN_NAME_MAX 16 static int write_to_flash(int flash_type, uint32_t address, uint32_t offset, uint32_t part_size, uint32_t file_size, char *layout) { char runcmd[256]; int nand_dev = CONFIG_NAND_FLASH_INFO_IDX; if (((flash_type == SMEM_BOOT_NAND_FLASH) || (flash_type == SMEM_BOOT_QSPI_NAND_FLASH))) { snprintf(runcmd, sizeof(runcmd), "nand device %d && ", nand_dev); if (strcmp(layout, "default") != 0) { snprintf(runcmd + strlen(runcmd), sizeof(runcmd), "ipq_nand %s && ", layout); } snprintf(runcmd + strlen(runcmd), sizeof(runcmd), "nand erase 0x%x 0x%x && " "nand write 0x%x 0x%x 0x%x && ", offset, part_size, address, offset, file_size); } else if (flash_type == SMEM_BOOT_MMC_FLASH) { snprintf(runcmd, sizeof(runcmd), "mmc erase 0x%x 0x%x && " "mmc write 0x%x 0x%x 0x%x && ", offset, part_size, address, offset, file_size); } else if (flash_type == SMEM_BOOT_SPI_FLASH) { snprintf(runcmd, sizeof(runcmd), "sf probe && " "sf erase 0x%x 0x%x && " "sf write 0x%x 0x%x 0x%x && ", offset, part_size, address, offset, file_size); } if (run_command(runcmd, 0) != CMD_RET_SUCCESS) return CMD_RET_FAILURE; return CMD_RET_SUCCESS; } static int fl_erase(int flash_type, uint32_t offset, uint32_t part_size, char *layout) { char runcmd[256]; int nand_dev = CONFIG_NAND_FLASH_INFO_IDX; if (((flash_type == SMEM_BOOT_NAND_FLASH) || (flash_type == SMEM_BOOT_QSPI_NAND_FLASH))) { snprintf(runcmd, sizeof(runcmd), "nand device %d && ", nand_dev); if (strcmp(layout, "default") != 0) { snprintf(runcmd + strlen(runcmd), sizeof(runcmd), "ipq_nand %s && ", layout); } snprintf(runcmd + strlen(runcmd), sizeof(runcmd), "nand erase 0x%x 0x%x ", offset, part_size); } else if (flash_type == SMEM_BOOT_MMC_FLASH) { snprintf(runcmd, sizeof(runcmd), "mmc erase 0x%x 0x%x ", offset, part_size); } else if (flash_type == SMEM_BOOT_SPI_FLASH) { snprintf(runcmd, sizeof(runcmd), "sf probe && " "sf erase 0x%x 0x%x ", offset, part_size); } if (run_command(runcmd, 0) != CMD_RET_SUCCESS) return CMD_RET_FAILURE; return CMD_RET_SUCCESS; } #ifdef IPQ_UBI_VOL_WRITE_SUPPORT int ubi_vol_present(char* ubi_vol_name) { int i; int j=0; struct ubi_device *ubi; struct ubi_volume *vol; if (ubi_set_rootfs_part()) return 0; ubi = ubi_devices[0]; for (i = 0; ubi && i < (ubi->vtbl_slots + 1); i++) { vol = ubi->volumes[i]; if (!vol) continue; /* Empty record */ if (vol->name_len <= UBI_VOL_NAME_MAX && strnlen(vol->name, vol->name_len + 1) == vol->name_len) { j++; if (!strncmp(ubi_vol_name, vol->name, UBI_VOL_NAME_MAX)) { return 1; } } if (j == ubi->vol_count - UBI_INT_VOL_COUNT) break; } printf("volume or partition %s not found\n", ubi_vol_name); return 0; } int write_ubi_vol(char* ubi_vol_name, uint32_t load_addr, uint32_t file_size) { char runcmd[256]; if (!strncmp(ubi_vol_name, "ubi_rootfs", UBI_VOL_NAME_MAX)) { snprintf(runcmd, sizeof(runcmd), "ubi remove rootfs_data &&" "ubi remove %s &&" "ubi create %s 0x%x &&" "ubi write 0x%x %s 0x%x &&" "ubi create rootfs_data", ubi_vol_name, ubi_vol_name, file_size, load_addr, ubi_vol_name, file_size); } else { snprintf(runcmd, sizeof(runcmd), "ubi write 0x%x %s 0x%x ", load_addr, ubi_vol_name, file_size); } return run_command(runcmd, 0); } #endif static int do_flash(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int flash_cmd = 0; uint32_t offset, part_size, adj_size; uint32_t load_addr = 0; uint32_t file_size = 0; uint32_t size_block, start_block, file_size_cpy; char *part_name = NULL, *filesize, *loadaddr; int flash_type, ret, retn; unsigned int active_part = 0; char *layout = NULL; #ifdef CONFIG_IPQ806X char* layout_linux[] = {"rootfs", "0:BOOTCONFIG", "0:BOOTCONFIG1"}; int len, i; #endif offset = 0; part_size = 0; layout = "default"; retn = CMD_RET_FAILURE; #ifdef CONFIG_QCA_MMC block_dev_desc_t *blk_dev; #endif disk_partition_t disk_info = {0}; qca_smem_flash_info_t *sfi = &qca_smem_flash_info; #ifdef CONFIG_CMD_NAND nand_info_t *nand = &nand_info[CONFIG_NAND_FLASH_INFO_IDX]; #endif if (strcmp(argv[0], "flash") == 0) flash_cmd = 1; if (flash_cmd) { if ((argc < 2) || (argc > 4)) return CMD_RET_USAGE; if (argc == 2) { loadaddr = getenv("fileaddr"); if (loadaddr != NULL) load_addr = simple_strtoul(loadaddr, NULL, 16); else return CMD_RET_USAGE; filesize = getenv("filesize"); if (filesize != NULL) file_size = simple_strtoul(filesize, NULL, 16); else return CMD_RET_USAGE; } else if (argc == 4) { load_addr = simple_strtoul(argv[2], NULL, 16); file_size = simple_strtoul(argv[3], NULL, 16); } else return CMD_RET_USAGE; file_size_cpy = file_size; } else { if (argc != 2) return CMD_RET_USAGE; } flash_type = sfi->flash_type; part_name = argv[1]; if (((sfi->flash_type == SMEM_BOOT_NAND_FLASH) || (sfi->flash_type == SMEM_BOOT_QSPI_NAND_FLASH))) { ret = smem_getpart(part_name, &start_block, &size_block); if (ret) { #ifdef IPQ_UBI_VOL_WRITE_SUPPORT if (ubi_vol_present(part_name)) return write_ubi_vol(part_name, load_addr, file_size); #endif return retn; } offset = sfi->flash_block_size * start_block; part_size = sfi->flash_block_size * size_block; #ifdef CONFIG_IPQ806X len = sizeof(layout_linux)/sizeof(layout_linux[0]); for (i = 0; i < len; i++) { if (!strncmp(layout_linux[i], part_name, SMEM_PTN_NAME_MAX)) { layout = "linux"; break; } } if (i == len ) layout = "sbl"; #endif #ifdef CONFIG_QCA_MMC } else if (sfi->flash_type == SMEM_BOOT_MMC_FLASH) { blk_dev = mmc_get_dev(mmc_host.dev_num); if (blk_dev != NULL) { ret = get_partition_info_efi_by_name(blk_dev, part_name, &disk_info); if (ret) return retn; offset = (ulong)disk_info.start; part_size = (ulong)disk_info.size; } #endif } else if (sfi->flash_type == SMEM_BOOT_SPI_FLASH) { if (get_which_flash_param(part_name)) { /* NOR + NAND*/ flash_type = SMEM_BOOT_NAND_FLASH; ret = getpart_offset_size(part_name, &offset, &part_size); if (ret) return retn; } else if (((sfi->flash_secondary_type == SMEM_BOOT_NAND_FLASH)|| (sfi->flash_secondary_type == SMEM_BOOT_QSPI_NAND_FLASH)) && (strncmp(part_name, "rootfs", 6) == 0)) { flash_type = sfi->flash_secondary_type; if (sfi->rootfs.offset == 0xBAD0FF5E) { if (smem_bootconfig_info() == 0) active_part = get_rootfs_active_partition(); offset = (ulong) active_part * IPQ_NAND_ROOTFS_SIZE; part_size = (ulong) IPQ_NAND_ROOTFS_SIZE; } #ifdef CONFIG_QCA_MMC } else if ((smem_getpart(part_name, &start_block, &size_block) == -ENOENT) && (sfi->rootfs.offset == 0xBAD0FF5E)){ /* NOR + EMMC */ flash_type = SMEM_BOOT_MMC_FLASH; blk_dev = mmc_get_dev(mmc_host.dev_num); if (blk_dev != NULL) { ret = get_partition_info_efi_by_name(blk_dev, part_name, &disk_info); if (ret) return retn; offset = (ulong)disk_info.start; part_size = (ulong)disk_info.size; } #endif } else { ret = smem_getpart(part_name, &start_block, &size_block); if (ret) { #ifdef IPQ_UBI_VOL_WRITE_SUPPORT if (ubi_vol_present(part_name)) return write_ubi_vol(part_name, load_addr, file_size); #endif return retn; } offset = sfi->flash_block_size * start_block; part_size = sfi->flash_block_size * size_block; } } if (flash_cmd) { #ifdef CONFIG_CMD_NAND if (((flash_type == SMEM_BOOT_NAND_FLASH) || (flash_type == SMEM_BOOT_QSPI_NAND_FLASH))) { adj_size = file_size % nand->writesize; if (adj_size) file_size = file_size + (nand->writesize - adj_size); } #endif if (flash_type == SMEM_BOOT_MMC_FLASH) { if (disk_info.blksz) { file_size = file_size / disk_info.blksz; adj_size = file_size_cpy % disk_info.blksz; if (adj_size) file_size = file_size + 1; } } if (file_size > part_size) { printf("Image size is greater than partition memory\n"); return CMD_RET_FAILURE; } ret = write_to_flash(flash_type, load_addr, offset, part_size, file_size, layout); } else ret = fl_erase(flash_type, offset, part_size, layout); return ret; } U_BOOT_CMD( flash, 4, 0, do_flash, "flash part_name \n" "\tflash part_name load_addr file_size \n", "flash the image at load_addr, given file_size in hex\n" ); U_BOOT_CMD( flasherase, 4, 0, do_flash, "flerase part_name \n", "erases on flash the given partition \n" );