| /* |
| * Copyright (C) 2017 Amlogic, 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 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. |
| * * |
| Description: |
| */ |
| |
| /* cmd c files for aml mtd, overide amlnf cmds */ |
| #include <config.h> |
| #include <common.h> |
| #include <command.h> |
| #include <environment.h> |
| #include <nand.h> |
| #include "aml_mtd.h" |
| |
| /* debug macros */ |
| #define CONFIG_AML_MTD_DBG (1) |
| #ifdef CONFIG_AML_MTD_DBG |
| static void dump_args(int argc, char * const argv[]) |
| { |
| int i; |
| /* debug codes for mtd cmd */ |
| for (i = 0; i < argc; i++) |
| printk("arg %d: %s\n", i, argv[i]); |
| |
| return; |
| } |
| #else |
| static void dump_args(int argc, char * const argv[]) |
| { |
| return; |
| } |
| #endif |
| |
| /* |
| * operations for bootloader |
| * we call it rom as legarcy reasons. |
| * call nand's opeartions. |
| * switch to normal device after doing this. |
| */ |
| #define CONFIG_AMLMTD_CURRDEV (0) |
| extern int set_mtd_dev(int dev); |
| extern int get_mtd_dev(void); |
| static int do_rom_ops(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) |
| { |
| int ret = 0; |
| int copy_num = 4; |
| int i; |
| #ifdef CONFIG_DISCRETE_BOOTLOADER |
| ulong cpy; |
| #endif |
| char *sub; |
| nand_info_t *nand; |
| unsigned long addr; |
| int base = 2; |
| u64 off, maxsize; |
| size_t rwsize, limit, wsize; |
| /* fixme, using this?! */ |
| #if (CONFIG_AMLMTD_CURRDEV) |
| int curr_mtd_dev; |
| #endif |
| printk("%s(): argc %d\n", __func__, argc); |
| dump_args(argc, argv); |
| #if (CONFIG_AMLMTD_CURRDEV) |
| curr_mtd_dev = get_mtd_dev(); |
| if (curr_mtd_dev != 0) |
| set_mtd_dev(0); |
| #endif |
| nand = &nand_info[0]; |
| maxsize = nand->size; |
| if (strlen(argv[1]) > 3) |
| sub = &argv[1][4]; |
| else { |
| sub = argv[2]; |
| base = 3; |
| } |
| if (!strcmp("read", sub)) { |
| printk("%s() %s\n", __func__, sub); |
| if (argc - base < 3) { |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| addr = (ulong)simple_strtoul(argv[base], NULL, 16); |
| off = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| rwsize = (ulong)simple_strtoul(argv[base + 2], NULL, 16); |
| ret = nand_read_skip_bad(nand, off, &rwsize, |
| NULL, maxsize, |
| (u8 *)addr); |
| } else if (!strcmp("write", sub)) { |
| printk("%s() %s\n", __func__, sub); |
| if (argc - base < 2) { |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| addr = (ulong)simple_strtoul(argv[base], NULL, 16); |
| #ifdef CONFIG_DISCRETE_BOOTLOADER |
| limit = nand->size / CONFIG_BL2_COPY_NUM; |
| /* write all copies if off do not exist */ |
| if (argc - base == 2) { |
| off = 0; |
| rwsize = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| copy_num = CONFIG_BL2_COPY_NUM; |
| } else { |
| off = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| rwsize = (ulong)simple_strtoul(argv[base + 2], NULL, 16); |
| copy_num = 1; |
| } |
| #else |
| /* write all, offset must be 0 */ |
| off = 0; |
| rwsize = (ulong)simple_strtoul(argv[base + 2], NULL, 16); |
| copy_num = get_boot_num(nand, rwsize); |
| limit = nand->size / copy_num; |
| #endif |
| printf("%s() %d\n", __func__, copy_num); |
| wsize = rwsize; |
| for (i = 0; i < copy_num; i++) { |
| ret = nand_write_skip_bad(nand, off, &rwsize, |
| NULL, limit, |
| (u8 *)addr, 0); |
| if (ret) |
| rwsize = wsize; |
| off += nand->size/copy_num; |
| } |
| } else if (!strcmp("erase", sub)) { |
| nand_erase_options_t opts; |
| printk("%s() %s\n", __func__, sub); |
| memset(&opts, 0, sizeof(opts)); |
| #ifdef CONFIG_DISCRETE_BOOTLOADER |
| if (argc - base == 0) { |
| opts.offset = 0; |
| /* whole boot area size */ |
| opts.length = nand->size; |
| } else { |
| cpy = (ulong)simple_strtoul(argv[base], NULL, 16); |
| copy_num = CONFIG_BL2_COPY_NUM; |
| if (cpy >= copy_num) { |
| printk("max cpies %d\n", copy_num); |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| opts.offset = nand->size / copy_num * cpy; |
| opts.length = nand->size / copy_num; |
| |
| } |
| #else |
| /* whole boot area size */ |
| opts.offset = 0; |
| opts.length = nand->size; |
| #endif |
| opts.jffs2 = 0; |
| opts.quiet = 0; |
| opts.spread = 0; |
| ret = nand_erase_opts(nand, &opts); |
| |
| } else { |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| |
| _out: |
| #if (CONFIG_AMLMTD_CURRDEV) |
| /* restore mtd device */ |
| if (curr_mtd_dev != 0) |
| set_mtd_dev(curr_mtd_dev); |
| #endif |
| return ret; |
| } |
| |
| #ifdef CONFIG_DISCRETE_BOOTLOADER |
| |
| /* bl2 operations */ |
| static int do_bl2_ops(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) |
| { |
| int ret = 0; |
| int copy_num = 4; |
| int i; |
| ulong cpy; |
| char *sub; |
| nand_info_t *nand; |
| unsigned long addr; |
| int base = 2; |
| u64 off, maxsize; |
| size_t rwsize, limit, wsize; |
| /* fixme, using this?! */ |
| #if (CONFIG_AMLMTD_CURRDEV) |
| int curr_mtd_dev; |
| #endif |
| printk("%s(): argc %d\n", __func__, argc); |
| dump_args(argc, argv); |
| #if (CONFIG_AMLMTD_CURRDEV) |
| curr_mtd_dev = get_mtd_dev(); |
| if (curr_mtd_dev != 0) |
| set_mtd_dev(0); |
| #endif |
| nand = &nand_info[0]; |
| maxsize = nand->size; |
| limit = maxsize / CONFIG_BL2_COPY_NUM; |
| if (strlen(argv[1]) > 3) |
| sub = &argv[1][4]; |
| else { |
| sub = argv[2]; |
| base = 3; |
| } |
| if (!strcmp("info", sub)) { |
| printk("bl2 infos:\ncopies %d\n", CONFIG_BL2_COPY_NUM); |
| } else if (!strcmp("read", sub)) { |
| printk("%s() %s\n", __func__, sub); |
| if (argc - base < 3) { |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| addr = (ulong)simple_strtoul(argv[base], NULL, 16); |
| cpy = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| rwsize = (ulong)simple_strtoul(argv[base + 2], NULL, 16); |
| off = cpy * limit; |
| ret = nand_read_skip_bad(nand, off, &rwsize, |
| NULL, limit, |
| (u8 *)addr); |
| } else if (!strcmp("write", sub)) { |
| printk("%s() %s\n", __func__, sub); |
| if (argc - base < 2) { |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| addr = (ulong)simple_strtoul(argv[base], NULL, 16); |
| /* write all copies if off do not exist */ |
| if (argc - base == 2) { |
| off = 0; |
| rwsize = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| copy_num = CONFIG_BL2_COPY_NUM; |
| } else { |
| cpy = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| off = cpy * limit; |
| rwsize = (ulong)simple_strtoul(argv[base + 2], NULL, 16); |
| copy_num = 1; |
| } |
| printf("%s() %d\n", __func__, copy_num); |
| wsize = rwsize; |
| for (i = 0; i < copy_num; i++) { |
| ret = nand_write_skip_bad(nand, off, &rwsize, |
| NULL, limit, |
| (u8 *)addr, 0); |
| if (ret) |
| rwsize = wsize; |
| off += nand->size/copy_num; |
| } |
| } else if (!strcmp("erase", sub)) { |
| nand_erase_options_t opts; |
| printk("%s() %s\n", __func__, sub); |
| memset(&opts, 0, sizeof(opts)); |
| if (argc - base == 0) { |
| opts.offset = 0; |
| /* whole boot area size */ |
| opts.length = nand->size; |
| } else { |
| copy_num = CONFIG_BL2_COPY_NUM; |
| cpy = (ulong)simple_strtoul(argv[base], NULL, 16); |
| if (cpy >= copy_num) { |
| printk("max cpies %d\n", copy_num); |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| opts.offset = nand->size / copy_num * cpy; |
| opts.length = nand->size / copy_num; |
| } |
| printf("%s, off 0x%llx, len 0x%llx\n", __func__, opts.offset, opts.length); |
| |
| opts.jffs2 = 0; |
| opts.quiet = 0; |
| opts.spread = 0; |
| ret = nand_erase_opts(nand, &opts); |
| |
| } else { |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| |
| _out: |
| #if (CONFIG_AMLMTD_CURRDEV) |
| /* restore mtd device */ |
| if (curr_mtd_dev != 0) |
| set_mtd_dev(curr_mtd_dev); |
| #endif |
| return ret; |
| } |
| |
| static int do_fip_ops(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) |
| { |
| int ret = 0; |
| int copy_num = 1; |
| int i; |
| char *sub; |
| nand_info_t *nand; |
| ulong addr; |
| ulong cpy; |
| int base = 2; |
| u64 off, maxsize = CONFIG_TPL_SIZE_PER_COPY*CONFIG_TPL_COPY_NUM; |
| u64 fip_base; |
| size_t rwsize, wsize; |
| |
| /* fixme, using this?! */ |
| #if (CONFIG_AMLMTD_CURRDEV) |
| int curr_mtd_dev; |
| #endif |
| printk("%s(): argc %d\n", __func__, argc); |
| dump_args(argc, argv); |
| #if (CONFIG_AMLMTD_CURRDEV) |
| curr_mtd_dev = get_mtd_dev(); |
| if (curr_mtd_dev != 0) |
| set_mtd_dev(0); |
| #endif |
| nand = &nand_info[1]; |
| if (strlen(argv[1]) > 3) |
| sub = &argv[1][4]; |
| else { |
| sub = argv[2]; |
| base = 3; |
| } |
| fip_base = 1024*((u64)nand->writesize) + \ |
| RESERVED_BLOCK_NUM*((u64)nand->erasesize); |
| if (!strcmp("read", sub)) { |
| printk("%s() %s\n", __func__, sub); |
| if (argc - base < 2) { |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| addr = (ulong)simple_strtoul(argv[base], NULL, 16); |
| off = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| rwsize = (ulong)simple_strtoul(argv[base + 2], NULL, 16); |
| off +=fip_base; |
| ret = nand_read_skip_bad(nand, |
| off, &rwsize, NULL, maxsize, (u8 *)addr); |
| } else if (!strcmp("write", sub)) { |
| printk("%s() %s\n", __func__, sub); |
| if (argc - base < 2) { |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| addr = (ulong)simple_strtoul(argv[base], NULL, 16); |
| if (argc - base == 2) { |
| off = fip_base; |
| rwsize = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| copy_num = CONFIG_TPL_COPY_NUM; |
| printk("%s %d: off=0x%llx rwsize=0x%zx\n", |
| __func__, __LINE__, off, rwsize); |
| |
| } else { |
| //addr off size |
| off = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| off += fip_base; |
| rwsize = (ulong)simple_strtoul(argv[base + 2], NULL, 16); |
| copy_num = 1; |
| } |
| if (rwsize > CONFIG_TPL_SIZE_PER_COPY) { |
| printk("size %ld > max per cpy %d\n", rwsize, CONFIG_TPL_COPY_NUM); |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| wsize = rwsize; |
| /* fixme, write it once! */ |
| for (i = 0; i < copy_num; i++) { |
| printk("cpy %d\n", i); |
| ret = nand_write_skip_bad(nand, |
| off, &rwsize, NULL, CONFIG_TPL_SIZE_PER_COPY, (u8 *)addr, 0); |
| if (ret) |
| rwsize = wsize; |
| off += CONFIG_TPL_SIZE_PER_COPY; |
| } |
| |
| } else if (!strcmp("erase", sub)) { |
| nand_erase_options_t opts; |
| printk("%s() %s, base %d\n", __func__, sub, base); |
| memset(&opts, 0, sizeof(opts)); |
| if (argc - base == 0) { |
| opts.offset = fip_base; |
| /* whole boot area size */ |
| opts.length = maxsize; |
| } else { |
| cpy = (ulong)simple_strtoul(argv[base], NULL, 16); |
| if (cpy >= CONFIG_TPL_COPY_NUM) { |
| printk("max cpies %d\n", CONFIG_TPL_COPY_NUM); |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| opts.offset = fip_base + cpy * CONFIG_TPL_SIZE_PER_COPY; |
| opts.length = CONFIG_TPL_SIZE_PER_COPY; |
| } |
| opts.jffs2 = 0; |
| opts.quiet = 0; |
| opts.spread = 0; |
| ret = nand_erase_opts(nand, &opts); |
| } else if (!strcmp("info", sub)) { |
| printk("tpl infos:\ncopies %d, size/copy 0x%x\n", |
| CONFIG_TPL_COPY_NUM, CONFIG_TPL_SIZE_PER_COPY); |
| } else{ |
| ret = CMD_RET_USAGE; |
| goto _out; |
| } |
| _out: |
| #if (CONFIG_AMLMTD_CURRDEV) |
| /* restore mtd device */ |
| if (curr_mtd_dev != 0) |
| set_mtd_dev(curr_mtd_dev); |
| #endif |
| return ret; |
| } |
| #endif |
| /* |
| * operations for dtb. |
| */ |
| extern int amlnf_dtb_read(u8 *buf, int len); |
| static int do_dtb_ops(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) |
| { |
| int ret = 0; |
| printk("%s(): argc %d\n", __func__, argc); |
| dump_args(argc, argv); |
| char *sub; |
| int base = 2; |
| unsigned long addr; |
| u64 size = 0; |
| |
| if (strlen(argv[1]) > 3) |
| sub = &argv[1][4]; |
| else { |
| sub = argv[2]; |
| base = 3; |
| } |
| |
| if (!strcmp("read", sub)) { |
| printk("%s() %s\n", __func__, sub); |
| addr = (ulong)simple_strtoul(argv[base], NULL, 16); |
| size = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| ret = amlnf_dtb_read((u8 *)addr, (int)size); |
| printk("%s(): %llu bytes %s : %s\n", |
| __func__, |
| size, |
| sub, |
| ret ? "ERROR" : "OK"); |
| } else if (!strcmp("write", sub)) { |
| printk("%s() %s\n", __func__, sub); |
| addr = (ulong)simple_strtoul(argv[base], NULL, 16); |
| size = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| ret = amlnf_dtb_save((u8 *)addr, (unsigned int)size); |
| printk("%s(): %llu bytes %s : %s\n", |
| __func__, |
| size, |
| sub, |
| ret ? "ERROR" : "OK"); |
| } else if (!strcmp("erase", sub)) { |
| printk("%s() %s\n", __func__, sub); |
| ret = amlnf_dtb_erase(); |
| printk("%s() erase %s\n", __func__, ret ? "Fail" : "Okay"); |
| } else |
| return CMD_RET_USAGE; |
| |
| return ret; |
| } |
| |
| /* |
| * operations for key. |
| * should never be used by users, just for nand team debug. |
| */ |
| extern int amlnf_key_read(u8 *buf, int len, uint32_t *actual_lenth); |
| extern int amlnf_key_write(u8 *buf, int len, uint32_t *actual_lenth); |
| extern int amlnf_key_erase(void); |
| static int do_key_ops(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) |
| { |
| int ret = 0; |
| printk("%s(): argc %d\n", __func__, argc); |
| dump_args(argc, argv); |
| char *sub; |
| int base = 2; |
| unsigned long addr; |
| u64 size = 0; |
| uint32_t len; |
| |
| if (strlen(argv[1]) > 3) |
| sub = &argv[1][4]; |
| else { |
| sub = argv[2]; |
| base = 3; |
| } |
| |
| if (!strcmp("read", sub)) { |
| printk("%s() %s\n", __func__, sub); |
| addr = (ulong)simple_strtoul(argv[base], NULL, 16); |
| size = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| ret = amlnf_key_read((u8 *)addr, (int)size, &len); |
| printk("%s(): %llu bytes %s : %s\n", |
| __func__, |
| size, |
| sub, |
| ret ? "ERROR" : "OK"); |
| } else if (!strcmp("write", sub)) { |
| printk("%s() %s\n", __func__, sub); |
| addr = (ulong)simple_strtoul(argv[base], NULL, 16); |
| size = (ulong)simple_strtoul(argv[base + 1], NULL, 16); |
| ret = amlnf_key_write((u8 *)addr, (int)size, &len); |
| printk("%s(): %llu bytes %s : %s\n", |
| __func__, |
| size, |
| sub, |
| ret ? "ERROR" : "OK"); |
| } else if (!strcmp("erase", sub)) { |
| printk("%s() %s\n", __func__, sub); |
| ret = amlnf_key_erase(); |
| printk("%s() erase %s\n", __func__, ret ? "Fail" : "Okay"); |
| } else |
| return CMD_RET_USAGE; |
| |
| return ret; |
| } |
| |
| static cmd_tbl_t cmd_amlmtd_sub[] = { |
| U_BOOT_CMD_MKENT(rom, 5, 0, do_rom_ops, "", ""), |
| #ifdef CONFIG_DISCRETE_BOOTLOADER |
| U_BOOT_CMD_MKENT(bl2, 5, 0, do_bl2_ops, "", ""), |
| U_BOOT_CMD_MKENT(fip, 5, 0, do_fip_ops, "", ""), |
| #endif |
| U_BOOT_CMD_MKENT(dtb, 5, 0, do_dtb_ops, "", ""), |
| U_BOOT_CMD_MKENT(key, 5, 0, do_key_ops, "", ""), |
| }; |
| |
| static int do_amlmtd(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) |
| { |
| cmd_tbl_t *c; |
| char subcmd[4]; |
| |
| if (argc < 2) return CMD_RET_USAGE; |
| /* process subcmd which is longger than 3 characaters */ |
| c = find_cmd_tbl(argv[1], cmd_amlmtd_sub, ARRAY_SIZE(cmd_amlmtd_sub)); |
| if (!c) { |
| strncpy(subcmd, argv[1], 3); |
| if (strlen(argv[1]) > 3) { |
| subcmd[3] = 0; |
| } |
| printk("sub cmd %s\n", subcmd); |
| c = find_cmd_tbl(subcmd, cmd_amlmtd_sub, ARRAY_SIZE(cmd_amlmtd_sub)); |
| if (c) { |
| printf("new argv[1] %s\n", argv[1]); |
| return c->cmd(cmdtp, flag, argc, argv); |
| } |
| } else { |
| return c->cmd(cmdtp, flag, argc, argv); |
| } |
| |
| return CMD_RET_USAGE; |
| } |
| |
| |
| #ifdef CONFIG_SYS_LONGHELP |
| static char amlmtd_help_text[] = |
| #ifndef CONFIG_DISCRETE_BOOTLOADER |
| "amlnf rom_read addr off size - read uboot by offset.\n" |
| "amlnf rom_write addr off size - write all uboot at once.\n" |
| "amlnf rom_erase - erase whole boot area\n" |
| #else |
| #if 0 /* hide for interal usage */ |
| "amlnf rom_erase [cpy] - erase bl2 area, erase all without cpy!\n" |
| "amlnf rom_read addr off size - read bl2 by offset.\n" |
| "amlnf rom_write addr [off] size - write bl2.\n" |
| "\t[off] inside offset\n\twirte all copies if without off\n" |
| #endif |
| "amlnf bl2_info - show bl2 infos\n" |
| "amlnf bl2_erase [cpy] - erase bl2 area, erase all without cpy!\n" |
| "amlnf bl2_read addr cpy size - read bl2 by cpy.\n" |
| "amlnf bl2_write addr [cpy] size - write bl2.\n" |
| "\t[cpy] copy to operate\n\twirte all copies if without cpy\n" |
| "amlnf fip_info - show fip infos\n" |
| "amlnf fip_read addr off size - read fip.\n" |
| "amlnf fip_write addr [off] size - write fip.\n" |
| "\t[off] inside offset\n\twirte all copies if without off\n" |
| "amlnf fip_erase [cpy] - erase fip area, erase all without cpy!\n" |
| #endif |
| "amlnf dtb_read/write addr size - read/write dtd.\n" |
| "amlnf dtb_erase - erase dtb area!\n" |
| "amlnf key_read/write addr size - read/write keys.\n" |
| "amlnf key_erase - erase keys!\n" |
| ""; |
| #endif |
| U_BOOT_CMD( |
| amlnf, CONFIG_SYS_MAXARGS, 0, do_amlmtd, |
| "aml mtd nand sub-system", |
| amlmtd_help_text |
| ); |