// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include "../include/amlnf_dev.h" /* #include //include in common.h */ /*#include */ #ifdef AML_NAND_UBOOT extern void amlnf_disprotect(char *name); extern struct amlnand_phydev *aml_phy_get_dev(const char * name); extern struct amlnf_dev* aml_nftl_get_dev(const char * name); extern void amlnf_get_chip_size(u64 *size); extern void show_phydev_list(void); extern void show_ldev_list(void); #endif extern void amlnf_dump_chipinfo(void); extern int roomboot_nand_read(struct amlnand_phydev *phydev); extern int roomboot_nand_write(struct amlnand_phydev *phydev); extern int nand_read_ops(struct amlnand_phydev *phydev); extern int nand_write_ops(struct amlnand_phydev *phydev); extern int nand_erase(struct amlnand_phydev *phydev); extern void amlnand_dump_page(struct amlnand_phydev *phydev); extern int amlnf_erase_ops(uint64_t off, uint64_t erase_len,unsigned char scrub_flag); extern int amlnf_markbad_reserved_ops(uint32_t start_blk); extern u8 amlnf_boot_cpys(const char *part_name); extern u64 amlnf_boot_copy_size(const char *part_name); #if (AML_CFG_DTB_RSV_EN) extern int amlnf_dtb_save(u8 *buf, int len); extern int amlnf_dtb_read(u8 *buf, int len); extern int amlnf_dtb_erase(void); #endif #if (AML_CFG_KEY_RSV_EN) extern int amlnf_key_write(u8 *buf, int len, uint32_t *actual_length); extern int amlnf_key_read(u8 * buf, int len, uint32_t *actual_length); extern int amlnf_key_erase(void); #endif extern int amlnf_init(unsigned char flag); extern int amlnf_exit(void); //static int plane_mode; struct amlnf_dev * nftl_device = NULL; struct amlnand_phydev *phy_device=NULL; static int nand_protect = 1; 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; } #ifdef AML_NAND_UBOOT /* * repack. **/ #if 0 unsigned long strtoul(const char *cp, char **endp, unsigned int base) { return simple_strtoul(cp, endp, base); } #endif #endif /* AML_NAND_UBOOT */ #if 0 static inline int str2long(char *p, ulong *num) { char *endptr; *num = strtoul(p, &endptr, 16); return (*p != '\0' && *endptr == '\0') ? 1 : 0; } #endif static inline int str2longlong(char *p, u64 *num) { char *endptr; #ifndef AML_NAND_UBOOT *num = strtoull(p, &endptr, 16); #else *num = simple_strtoul(p, &endptr, 16); #endif if (*endptr != '\0') { switch (*endptr) { case 'g': case 'G': *num <<= 10; case 'm': case 'M': *num <<= 10; case 'k': case 'K': *num <<= 10; endptr++; break; } } return (*p != '\0' && *endptr == '\0') ? 1 : 0; } static int arg_off_size(int argc, char *argv[], u64 chipsize, u64 *off, u64 *size) { if (argc >= 1) { if (!(str2longlong(argv[0], (u64 *)off))) { aml_nand_msg("'%s' is not a number", argv[0]); return -1; } } else *off = 0; if (argc >= 2) { if (!(str2longlong(argv[1], (u64 *)size))) { aml_nand_dbg("'%s' is not a number", argv[1]); return -1; } } else *size = chipsize - *off; if (*size == chipsize) aml_nand_msg("whole chip/dev"); else aml_nand_msg("offset 0x%llx, size 0x%llx", *off, *size); return 0; } #define AML_NFTL_ALIGN_SIZE 512 #define AML_NFTL_ALIGN_SHIFT 9 u8 local_buf[AML_NFTL_ALIGN_SIZE]; int amlnand_read(struct amlnf_dev *nftl_dev, u8 *buf, u64 offset, /* in bytes */ u64 size) /* in bytes */ { u32 ret = 0; u64 head_sector; u64 head_start_bytes; u64 head_bytes_num; u64 mid_sector; u64 mid_len; u64 tail_sector; u64 tail_bytes_num; mid_len = offset >> AML_NFTL_ALIGN_SHIFT; /* in sector */ head_start_bytes = offset - (mid_len << AML_NFTL_ALIGN_SHIFT); head_bytes_num = AML_NFTL_ALIGN_SIZE - head_start_bytes; head_sector = offset >> AML_NFTL_ALIGN_SHIFT; CMD_LINE if (head_bytes_num >= size) { CMD_LINE ret |= nftl_dev->read_sector(nftl_dev, head_sector, 1, local_buf); memcpy(buf, local_buf+head_start_bytes, size); return ret; } CMD_LINE if ((offset % AML_NFTL_ALIGN_SIZE) != 0) { ret |= nftl_dev->read_sector(nftl_dev, head_sector, 1, local_buf); memcpy(buf, local_buf+head_start_bytes, head_bytes_num); CMD_LINE buf += head_bytes_num; offset += head_bytes_num; size -= head_bytes_num; } if (size > AML_NFTL_ALIGN_SIZE) { mid_len = size >> AML_NFTL_ALIGN_SHIFT; mid_sector = offset >> AML_NFTL_ALIGN_SHIFT; CMD_LINE ret |= nftl_dev->read_sector(nftl_dev, mid_sector, mid_len, buf); buf += mid_len << AML_NFTL_ALIGN_SHIFT; offset += mid_len << AML_NFTL_ALIGN_SHIFT; size = size - (mid_len << AML_NFTL_ALIGN_SHIFT); } if (size == 0) return ret; CMD_LINE tail_sector = offset >> AML_NFTL_ALIGN_SHIFT; tail_bytes_num = size; ret |= nftl_dev->read_sector(nftl_dev, tail_sector, 1, local_buf); memcpy(buf, local_buf, tail_bytes_num); CMD_LINE return ret; } int amlnand_write(struct amlnf_dev *nftl_dev, u8 *buf, u64 offset, u64 size) { u32 ret = 0; u64 head_sector; u64 head_start_bytes; u64 head_bytes_num; u64 mid_sector; u64 mid_len; u64 tail_sector; u64 tail_bytes_num; CMD_LINE mid_len = offset >> AML_NFTL_ALIGN_SHIFT; head_start_bytes = offset - (mid_len << AML_NFTL_ALIGN_SHIFT); head_bytes_num = AML_NFTL_ALIGN_SIZE - head_start_bytes; head_sector = offset >> AML_NFTL_ALIGN_SHIFT; CMD_LINE if (head_bytes_num >= size) { ret |= nftl_dev->read_sector(nftl_dev, head_sector, 1, local_buf); memcpy(local_buf+head_start_bytes, buf, size); ret |= nftl_dev->write_sector(nftl_dev, head_sector, 1, local_buf); goto flush; } CMD_LINE if ((offset % AML_NFTL_ALIGN_SIZE) != 0) { //sectore alignment ret |= nftl_dev->read_sector(nftl_dev, head_sector, 1, local_buf); memcpy(local_buf+head_start_bytes, buf, head_bytes_num); ret |= nftl_dev->write_sector(nftl_dev, head_sector, 1, local_buf); buf += head_bytes_num; offset += head_bytes_num; size -= head_bytes_num; } if (size > AML_NFTL_ALIGN_SIZE) { //why 4 CMD_LINE mid_len = size >> AML_NFTL_ALIGN_SHIFT; mid_sector = offset >> AML_NFTL_ALIGN_SHIFT; ret |= nftl_dev->write_sector(nftl_dev, mid_sector, mid_len, buf); buf += mid_len << AML_NFTL_ALIGN_SHIFT; offset += mid_len << AML_NFTL_ALIGN_SHIFT; size = size - (mid_len << AML_NFTL_ALIGN_SHIFT); } if (size == 0) goto flush; CMD_LINE tail_sector = offset >> AML_NFTL_ALIGN_SHIFT; tail_bytes_num = size; ret |= nftl_dev->read_sector(nftl_dev, tail_sector, 1, local_buf); memcpy(local_buf, buf, tail_bytes_num); ret |= nftl_dev->write_sector(nftl_dev, tail_sector, 1, local_buf); flush: CMD_LINE ret = nftl_dev->flush((struct amlnf_dev *)nftl_dev); if (ret < 0) { aml_nand_msg("nftl flush cache failed"); ret = -1; } CMD_LINE return ret; } /** * @usage: get size of the partition * * @name: part_name, when it's null the target * will return normal device size(nfcache, * nfcode,nfdata). * * @return: size of the partition * >0 : success * 0 : failed */ u64 amlnf_get_size(const char *part_name) { u64 size = 0; struct amlnf_dev *nftl_dev = NULL; /*struct amlnand_phydev *phydev = NULL;*/ if (!part_name) { #if 0 aml_nand_msg("part name null"); phydev = aml_phy_get_dev(NAND_CACHE_NAME); if (!phydev) { aml_nand_msg("nfcache phydev be NULL!"); return 0; } size += phydev->size; phydev = aml_phy_get_dev(NAND_CODE_NAME); if (!phydev) { aml_nand_msg("nfcode phydev be NULL!"); return 0; } size += phydev->size; phydev = aml_phy_get_dev(NAND_DATA_NAME); if (!phydev) { aml_nand_msg("nfdata phydev be NULL!"); return 0; } size += phydev->size; #endif amlnf_get_chip_size(&size); return size; } nftl_dev = aml_nftl_get_dev(part_name); if (!nftl_dev) { aml_nand_msg("nftl_dev be NULL"); return 0; } size = (nftl_dev->size_sector)<<9; aml_nand_msg("nftl_dev->name =%s, size: %lld", nftl_dev->name,size); return size; } /** * @usage: read data from normal device * * @part_name: part_name, when it's null the target * will regards as (nfcache) device. * @off: offset to the 0 address of partition/device * @size: the amount of bytes to read * @dest: pointer of target buffer * * @return: result of the operation * 0 = success * other = fail */ int amlnf_read(const char *part_name, loff_t off, size_t size,void *dest) { struct amlnf_dev *nftl_dev = NULL; struct amlnand_phydev *phydev = NULL; struct phydev_ops *devops = NULL; char ret = 0xff; if (!part_name) { aml_nand_msg("part name null"); phydev = aml_phy_get_dev(NAND_CACHE_NAME); devops = &(phydev->ops); memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = (u64)off; devops->len = (u64)size; devops->mode = NAND_HW_ECC; devops->datbuf = (u8 *)dest; if (devops->addr + devops->len > phydev->size) { aml_nand_msg("Attemp to read out side nfcache dev area"); return -1; } ret = nand_read_ops(phydev); if (ret < 0) aml_nand_msg("nand read failed"); return ret; } nftl_dev = aml_nftl_get_dev(part_name); if (!nftl_dev) { aml_nand_msg("nftl_dev be null"); return -1; } ret = amlnand_read(nftl_dev, (u8 *)dest, (u64)off, (u64)size); if (ret == 0) aml_nand_msg("read success"); return ret; } /** * @usage: write data to normal device * * @part_name: part_name, when it's null the target * will regards as (nfcache) device. * @off: offset to the 0 address of partition/device * @size: the amount of bytes to read * @source: pointer of target buffer * * @return: result of the operation * 0 = success * other = fail */ int amlnf_write(const char *part_name, loff_t off, size_t size, void *source) { struct amlnf_dev *nftl_dev = NULL; struct amlnand_phydev *phydev = NULL; struct phydev_ops *devops = NULL; char ret = 0xff; if (!part_name) { aml_nand_msg("part name null"); phydev = aml_phy_get_dev(NAND_CACHE_NAME); devops = &(phydev->ops); memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = (u64)off; devops->len = (u64)size; devops->mode = NAND_HW_ECC; devops->datbuf = (u8 *)source; if (devops->addr + devops->len > phydev->size) { aml_nand_msg("Attemp to write out side nfcache dev area"); return -1; } ret = nand_write_ops(phydev); if (ret < 0) aml_nand_msg("nand write failed"); return ret; } nftl_dev = aml_nftl_get_dev(part_name); if (!nftl_dev) { aml_nand_msg("nftl dev null"); return -1; } ret = amlnand_write(nftl_dev, (u8 *)source, (u64)off, (u64)size); if (!ret) aml_nand_msg("write success"); return ret; } /** * @usage: erase the phydev device * * @phydev_name: device(nfboot,nfcache,nfcode,nfdata). * @off: offset to the 0 address of partition/device * @size: the amount of bytes to erase * @check_flag: Is need to erase the bad blk * 0 = no erase * 1 = erase all bad blk * @return: result of the operation * 0 = success * other = fail */ int aml_phydev_erase(char *phydev_name, u64 off, u64 size, u8 check_flag) { struct amlnand_phydev *phydev = NULL; struct phydev_ops *devops = NULL; int percent = 0; int percent_complete = -1; char ret = 0xff; u64 erase_addr, erase_len, erase_off; phydev = aml_phy_get_dev(phydev_name); if (!phydev) { aml_nand_msg("phydev be null"); return -1; } devops = &(phydev->ops); erase_addr = erase_off = off; erase_len = size; aml_nand_msg("off1 : %llx, size1: %llx",off, size); if (erase_len < phydev->erasesize) { aml_nand_msg("erase size 0x%016llx smaller than one blk size 0x%08x", erase_len, phydev->erasesize); aml_nand_msg("erasing 0x%08x instead", phydev->erasesize); erase_len = phydev->erasesize; } for (; erase_addr < erase_off + erase_len; erase_addr += phydev->erasesize) { memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = erase_addr; devops->len = phydev->erasesize; devops->mode = NAND_HW_ECC; /* do not check bad block in uboot area! */ if (!check_flag) { ret = phydev->block_isbad(phydev); if (ret > 0) { aml_nand_msg("\rSkipping bad block at 0x%08llx", erase_addr); continue; } else if (ret < 0) { aml_nand_msg("get blk failed: ret= %d addr= %llx", ret, erase_addr); return -1; } } ret = nand_erase(phydev); if (ret < 0) { aml_nand_msg("\nAMLNAND Erase fail: %d %llx\n", ret, erase_addr); ret = phydev->block_markbad(phydev); if (ret < 0) aml_nand_msg("bad blk mark fail: %llx\n", erase_addr); continue; } percent = (erase_addr *100) / (erase_off + erase_len); if ((percent != percent_complete) && ((percent % 10) == 0)) { percent_complete = percent; aml_nand_msg("erase %d %%-%d %% complete", percent, percent+10); } } aml_nand_msg("NAND %s %s\n", "Erase", (ret < 0) ? "Erase" : "ok"); return 0; } /** * @usage: erase the normal device * * @part_name: partition name, when it's null the target * will regards as all normal device(nfcache, * nfcode,nfdata). * @off: offset to the 0 address of device * @size: the amount of bytes to erase * @scrub_flag: scrub flag(scrub operats will works only * when the device support) * 0 = no scrub, just erase * 1 = use scrub operats instead of erase,erase * all chipsize include bootloader * @return: result of the operation * 0 = success * other = fail */ int amlnf_erase(const char *part_name, loff_t off, size_t size, int scrub_flag) { struct amlnand_phydev *phydev = NULL; u8 check_flag = 0; char *dev_name = NULL; aml_nand_msg("amlnf erase Enter"); if (!part_name) { aml_nand_msg("part name null"); if (!size) { uint64_t chipsize = 0; amlnf_get_chip_size(&chipsize); amlnf_erase_ops(off, chipsize, scrub_flag); } else amlnf_erase_ops(off, size, scrub_flag); #if 0 list_for_each_entry(phydev, &nphy_dev_list,list){ if (phydev != NULL) { if (strncmp((char *)phydev->name, NAND_BOOT_NAME, strlen((const char*)NAND_BOOT_NAME))) { off = 0; size = phydev->size; aml_phydev_erase((char *)phydev->name, (u64)off, (u64)size, check_flag); } } } #endif return 0; } if (strcmp(part_name, "boot") == 0) { dev_name = NAND_BOOT_NAME; check_flag = 1; } else if (strcmp(part_name, "code") == 0) dev_name = NAND_CODE_NAME; else if (strcmp(part_name, "cache") == 0) dev_name = NAND_CACHE_NAME; else if (strcmp(part_name, "data") == 0) dev_name = NAND_DATA_NAME; else { aml_nand_msg("input wrong name!! %s",dev_name); return -1; } phydev = aml_phy_get_dev(dev_name); if (!phydev) { aml_nand_msg("phydev be NULL"); return -1; } if (size == 0) size = phydev->size; aml_phydev_erase(dev_name, (u64)off, (u64)size, check_flag); return 0; } /** * @usage: read the bootloader from storage device * * @name: only can be "bootloader" * @copy: which copy you want read,mlc support read the nst cpy * @size: the amount of bytes to read * @buf: pointer of the target buffer * * @return: result of the operation * 0 = success * other = fail */ int amlnf_boot_read(const char *part_name, uint8_t copy, size_t size, void *buf) { struct amlnand_phydev *phydev = NULL; char *dev_name = NULL; struct phydev_ops *devops = NULL; char ret = 0; if (strcmp(part_name, "bootloader") == 0) { dev_name = NAND_BOOT_NAME; } else { aml_nand_msg("no tpl"); ret = -1; return ret; } phydev = aml_phy_get_dev(dev_name); if (!phydev) { aml_nand_msg("phydev be NULL"); ret = -1; return ret; } devops = &(phydev->ops); memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = 0; devops->len = size; devops->mode = NAND_HW_ECC; devops->datbuf = buf; ret = roomboot_nand_read(phydev); if (ret < 0) { aml_nand_msg("nand read uboot failed"); } return ret; } /** * @usage: write the bootloaderinto storage device * * @name: only can be "bootloader" * @copy: which copy you want write, * it will write to all copies when copy = BOOT_OPS_ALL * @size: the amount of bytes to write * @buf: pointer of the source buffer * * @return: result of the operation * 0 = success * other = fail */ int amlnf_boot_write(const char *part_name, uint8_t copy, size_t size, void *buf) { struct amlnand_phydev *phydev = NULL; char *dev_name = NULL; struct phydev_ops *devops = NULL; char ret = 0; if (strcmp(part_name, "bootloader") == 0) { dev_name = NAND_BOOT_NAME; } else { aml_nand_msg("no tpl"); ret = -1; return ret; } phydev = aml_phy_get_dev(dev_name); if (!phydev) { aml_nand_msg("phydev be NULL"); ret = -1; return ret; } devops = &(phydev->ops); memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = 0; devops->len = size; devops->mode = NAND_HW_ECC; devops->datbuf = buf; ret = roomboot_nand_write(phydev); if (ret < 0) { aml_nand_msg("nand write uboot failed"); } return ret; } /** * @usage: erase the bootloader/ * * @name: only can be "bootloader" * @copy: it will erase all copies * * @return: result of the operation * 0 = success * other = fail */ int amlnf_boot_erase(const char *part_name, uint8_t copy) { struct amlnand_phydev *phydev = NULL; u8 check_flag = 0; char *dev_name =NULL; u64 off, size; char ret = 0xff; if (strcmp(part_name, "bootloader") == 0) { dev_name = NAND_BOOT_NAME; check_flag = 1; } else { aml_nand_msg("no tpl"); ret = -1; return ret; } phydev = aml_phy_get_dev(dev_name); if (!phydev) { aml_nand_msg("phydev be null"); return -1; } off =0; size = phydev->size; aml_phydev_erase(dev_name, off, size, check_flag); return 0; } extern void dbg_phyop(void); static int do_amlnfphy(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { struct amlnand_phydev *phydev = NULL; struct phydev_ops *devops = NULL; struct amlnf_dev *nftl_dev; unsigned long addr; u64 chipsize, erase_addr, erase_len, erase_off, off, size; u8 devread, nfread_flag; int start_secor, length, ret = 0; char *cmd, *protect_name; char *dev_name = NULL; unsigned long markbad_reserved_addr = 0; /* at least two arguments please */ if (argc < 1) goto usage; cmd = argv[1]; /* for dbg entry! */ if (strcmp(cmd, "dbg") == 0) { dbg_phyop(); return 0; } /* show boot flag!*/ if (strcmp(cmd, "boot") == 0) { aml_nand_msg("bootflag not use yet!"); return 0; } if (strcmp(cmd, "bootloader") == 0) { return 0; } if (strcmp(cmd, "device") == 0) { //printk("argc %d\n", argc); if (argc == 2) { show_phydev_list(); return 0; } return 0; } if (strcmp(cmd, "env") == 0) { aml_nand_dbg("env relocate"); env_relocate(); return 0; } if (strcmp(cmd, "disprotect") == 0) { protect_name = argv[2]; /* fixme, using amlnf_chip*/ amlnf_disprotect(protect_name); return 0; } if (strcmp(cmd, "exit") == 0) { amlnf_exit(); return 0; } if (strcmp(cmd, "init") == 0) { putc('\n'); /*int init_flag = (ulong)strtoul(argv[2], NULL, 16);*/ int init_flag = (ulong)simple_strtoul(argv[2], NULL, 16); /* flag = 0, indicate normal boot; */ /* flag = 1, indicate update, with data; */ /* flag = 2, indicate need erase */ aml_nand_msg("init_flag:%x",init_flag); ret = amlnf_init(init_flag); if (ret) { aml_nand_msg("nand_init failed ret:%x", ret); return ret; } if (init_flag == 5) { aml_nand_msg("amlnf pre return %d",ret); return ret; } phydev = aml_phy_get_dev(NAND_CODE_NAME); if (!phydev) { aml_nand_msg("phydev be NULL"); goto usage; } return 0; } //ldevice if (strcmp(cmd, "ldevice") == 0) { //printk("argc %d\n", argc); if (argc == 2) { show_ldev_list(); return 0; } //fixme, goto usage; return 0; } if ((strcmp(cmd, "read_byte") == 0) || (strcmp(cmd, "write_byte") == 0) || (strcmp(cmd, "lwrite") == 0) ||(strcmp(cmd, "lread") == 0)) { if (argc < 6) goto usage; dev_name = argv[2]; /*addr = (ulong)strtoul(argv[3], NULL, 16);*/ addr = (ulong)simple_strtoul(argv[3], NULL, 16); nfread_flag = 0; if ((strncmp(cmd, "read_byte", 9) == 0) ||(strncmp(cmd, "lread", 5) == 0) ) nfread_flag = 1; if (arg_off_size(argc - 4, (char **)(argv + 4), 0x0, &off, &size) != 0) { goto usage; } if (nfread_flag) ret = amlnf_read(dev_name, (loff_t)off, (size_t)size, (void *)addr); else ret = amlnf_write(dev_name, (loff_t)off, (size_t)size, (void *)addr); aml_nand_msg(" 0x%llx bytes %s : %s", size, nfread_flag ? "read_byte" : "write_byte", ret ? "ERROR" : "OK"); return ret; } if ((strcmp(cmd, "read") == 0) || (strcmp(cmd, "write") == 0)) { if (argc < 6) goto usage; dev_name = argv[2]; nftl_dev = NULL; nftl_dev = aml_nftl_get_dev(dev_name); if (!nftl_dev) { aml_nand_msg("nftl_dev be NULL"); return -1; } aml_nand_dbg("nftl_dev->nand_dev->writesize =%x", nftl_dev->nand_dev->writesize); aml_nand_dbg("nftl_dev->nand_dev->erasesize =%x", nftl_dev->nand_dev->erasesize); aml_nand_dbg("nftl_dev->name =%s", nftl_dev->name); /*addr = (ulong)strtoul(argv[3], NULL, 16);*/ addr = (ulong)simple_strtoul(argv[3], NULL, 16); nfread_flag = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ aml_nand_msg("NAND %s: addr:%lx", nfread_flag ? "read" : "write", addr); if (arg_off_size(argc - 4, (char **)(argv + 4), 0x0, &off, &size) != 0) goto usage; if (off % 512) { start_secor = ((int) (off / 512) + 1); aml_nand_dbg("secor+1"); } else start_secor = (int) (off / 512); if (size % 512) { length = ((int)((size / 512))+1); aml_nand_dbg("length+1"); } else length = (int)((size / 512)); aml_nand_dbg("start_secor =%d", start_secor); aml_nand_dbg("length_sector =%d", length); if (nfread_flag) { ret = nftl_dev->read_sector(nftl_dev, start_secor, length, (u8 *)addr); if (ret < 0) { aml_nand_dbg("read %d sector failed", length); return -1; } } else { ret = nftl_dev->write_sector(nftl_dev, start_secor, length, (u8 *)addr); if (ret < 0) { aml_nand_dbg("write %d sector failed", length); return -1; } ret = nftl_dev->flush(nftl_dev); if (ret < 0) { aml_nand_dbg("nftl flush cache failed"); return -1; } } aml_nand_msg(" %d sector %s : %s", length, nfread_flag ? "read" : "write", ret ? "ERROR" : "OK"); return ret; } if (strcmp(cmd, "chipinfo") == 0) { putc('\n'); amlnf_dump_chipinfo(); return 0; } if (strcmp(cmd, "size") == 0) { if (argc < 4) goto usage; dev_name = argv[2]; /*addr = (ulong)strtoul(argv[3], NULL, 16);*/ addr = (ulong)simple_strtoul(argv[3], NULL, 16); *(u64 *)addr = amlnf_get_size(dev_name); return 0; } if (strncmp(cmd, "rom_protect", 9) == 0) { if (argc < 2) goto usage; if (strncmp(argv[2], "on", 2) == 0) nand_protect = 1; else if (strncmp(argv[2], "off", 3) == 0) nand_protect = 0; else goto usage; return 0; } if ((strcmp(cmd, "rom_write") == 0) || (strcmp(cmd, "rom_read") == 0)) { nfread_flag = 0; if (strncmp(cmd, "rom_read", 8) == 0) nfread_flag = 1; if (argc < 4) goto usage; /*addr = (ulong)strtoul(argv[2], NULL, 16);*/ addr = (ulong)simple_strtoul(argv[2], NULL, 16); aml_nand_msg("AMLNAND %s: ", nfread_flag ? "rom_read" : "rom_write"); struct amlnand_phydev *tmp_phydev = phydev; struct phydev_ops *tmp_devops = devops; phydev = aml_phy_get_dev(NAND_BOOT_NAME); if (!phydev) { aml_nand_msg("phydev be NULL"); return -1; } devops = &(phydev->ops); aml_nand_dbg("phydev->name =%s", phydev->name); amldev_dumpinfo(phydev); if (arg_off_size(argc - 3, (char **)(argv + 3), phydev->size, &off, &size) != 0) return -1; memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = off; devops->len = size; devops->mode = NAND_HW_ECC; devops->datbuf = (u8 *)addr; if (nfread_flag) { ret = roomboot_nand_read(phydev); if (ret < 0) aml_nand_msg("nand read uboot failed"); } else { ret = roomboot_nand_write(phydev); if (ret < 0) aml_nand_msg("nand write uboot failed"); } aml_nand_msg("%llu bytes %s : %s", size, nfread_flag ? "rom_read" : "rom_write", ret ? "ERROR" : "OK"); phydev = tmp_phydev; devops = tmp_devops; return ret; } #if (AML_CFG_DTB_RSV_EN) if ((strcmp(cmd, "dtb_write") == 0) || (strcmp(cmd, "dtb_read") == 0)) { nfread_flag = 0; if (strncmp(cmd, "dtb_read", 8) == 0) nfread_flag = 1; if (argc < 3) goto usage; /*addr = (ulong)strtoul(argv[2], NULL, 16); size = (ulong)strtoul(argv[3], NULL, 16);*/ addr = (ulong)simple_strtoul(argv[2], NULL, 16); size = (ulong)simple_strtoul(argv[3], NULL, 16); aml_nand_msg("cmd %s: ", nfread_flag ? "dtb_read" : "dtb_write"); //memset(devops, 0x0, sizeof(struct phydev_ops)); if (nfread_flag) { ret = amlnf_dtb_read((u8 *)addr, (int)size); if (ret < 0) aml_nand_msg("nand read dtd failed"); } else { ret = amlnf_dtb_save((u8 *)addr, (int)size); if (ret < 0) aml_nand_msg("nand write dtd failed"); } aml_nand_msg("%llu bytes %s : %s", size, nfread_flag ? "dtd_read" : "dtd_write", ret ? "ERROR" : "OK"); return ret; } if (strcmp(cmd, "dtb_erase") == 0) { ret = amlnf_dtb_erase(); aml_nand_msg("dtb erase %s", ret ? "Fail" : "Okay"); return ret; } #endif /* need full environments */ #if (AML_CFG_KEY_RSV_EN) uint32_t actual_length; if ((strcmp(cmd, "key_write") == 0) || (strcmp(cmd, "key_read") == 0)) { nfread_flag = 0; if (strncmp(cmd, "key_read", 8) == 0) nfread_flag = 1; if (argc < 3) goto usage; /*addr = (ulong)strtoul(argv[2], NULL, 16); size = (ulong)strtoul(argv[3], NULL, 16);*/ addr = (ulong)simple_strtoul(argv[2], NULL, 16); size = (ulong)simple_strtoul(argv[3], NULL, 16); aml_nand_msg("cmd %s: ", nfread_flag ? "key_read" : "key_write"); //memset(devops, 0x0, sizeof(struct phydev_ops)); if (nfread_flag) { ret = amlnf_key_read((u8 *)addr, (int)size, &actual_length); if (ret < 0) aml_nand_msg("nand read key failed"); } else { ret = amlnf_key_write((u8 *)addr, (int)size, &actual_length); if (ret < 0) aml_nand_msg("nand write key failed"); } aml_nand_msg("%llu bytes %s : %s", size, nfread_flag ? "key_read" : "key_write", ret ? "ERROR" : "OK"); return ret; } if (strcmp(cmd, "key_erase") == 0) { ret = amlnf_key_erase(); aml_nand_msg("key erase %s", ret ? "Fail" : "Okay"); return ret; } #endif /* avoid fail... */ amlnf_get_chip_size(&chipsize); if ((strcmp(cmd, "devread") == 0) || (strcmp(cmd, "devwrite") == 0)) { if (argc < 6) goto usage; dev_name = argv[2]; if (strcmp(dev_name, "boot") == 0) dev_name = NAND_BOOT_NAME; else if (strcmp(dev_name, "code") == 0) dev_name = NAND_CODE_NAME; else if (strcmp(dev_name, "cache") == 0) dev_name = NAND_CACHE_NAME; else if (strcmp(dev_name, "data") == 0) dev_name = NAND_DATA_NAME; else { aml_nand_msg("input wrong name!! %s", dev_name); goto usage; } /*addr = (ulong)strtoul(argv[3], NULL, 16);*/ addr = (ulong)simple_strtoul(argv[3], NULL, 16); aml_nand_dbg("addr = %llx", addr); devread = strncmp(cmd, "devread", 7) == 0; /* 1 = devread, 0 = devwrite */ aml_nand_msg("NAND %s: addr:%lx", devread ? "devread" : "devwrite", addr); if (arg_off_size(argc - 4, (char **)(argv + 4), chipsize, &off, &size) != 0) goto usage; phydev = aml_phy_get_dev(dev_name); if (!phydev) { aml_nand_msg("phydev be NULL"); return -1; } devops = &(phydev->ops); memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = off; devops->len = size; devops->mode = NAND_HW_ECC; devops->datbuf = (u8 *)addr; if (devread) { ret = nand_read_ops(phydev); if (ret < 0) aml_nand_dbg("nand read failed"); } else { ret = nand_write_ops(phydev); if (ret < 0) aml_nand_dbg("nand write failed"); } aml_nand_msg("%llu bytes %s : %s", size, devread ? "devread" : "devwrite", ret ? "ERROR" : "OK"); return 0; } if ((strcmp(cmd, "deverase") == 0)) { if (argc < 4) goto usage; int without_check = 0; dev_name = argv[2]; if (strcmp(dev_name, "boot") == 0) { dev_name = NAND_BOOT_NAME; /* do not check bad block in uboot area! */ without_check = 1; } else if (strcmp(dev_name, "code") == 0) dev_name = NAND_CODE_NAME; else if (strcmp(dev_name, "cache") == 0) dev_name = NAND_CACHE_NAME; else if (strcmp(dev_name, "data") == 0) dev_name = NAND_DATA_NAME; else { aml_nand_msg("input wrong name!! %s", dev_name); goto usage; } phydev = aml_phy_get_dev(dev_name); if (!phydev) { aml_nand_msg("phydev be NULL"); return -1; } if (!strcmp(argv[3], "whole")) { off = 0; size = phydev->size; printf("whole dev.\n"); } else { if (argc < 3) goto usage; if ((arg_off_size(argc - 3, (char **)(argv + 3), phydev->size, &off, &size) != 0)) goto usage; //aml_nand_msg("off:0x%llx size:%llx.\n", off, size); } aml_nand_msg("off:0x%llx size:%llx.\n", off, size); aml_phydev_erase(dev_name, (u64)off, (u64)size, without_check); /*dev_name = argv[2]; amlnf_erase(dev_name, (loff_t)off, (size_t)size, without_check);*/ /*for test*/ return 0; } if (strcmp(cmd, "dump") == 0) { dev_name = argv[2]; if (strcmp(dev_name, "boot") == 0) dev_name = NAND_BOOT_NAME; else if (strcmp(dev_name, "cache") == 0) dev_name = NAND_CACHE_NAME; else if (strcmp(dev_name, "code") == 0) dev_name = NAND_CODE_NAME; else if (strcmp(dev_name, "data") == 0) dev_name = NAND_DATA_NAME; else { aml_nand_msg("input wrong name!! %s", dev_name); goto usage; } /*addr = (ulong)strtoul(argv[3], NULL, 16);*/ addr = (ulong)simple_strtoul(argv[3], NULL, 16); aml_nand_dbg("addr = %llx", addr); if (arg_off_size(argc - 4, (char **)(argv + 4), chipsize, &off, &size) != 0) goto usage; phydev = aml_phy_get_dev(dev_name); if (!phydev) { aml_nand_msg("phydev be NULL"); return -1; } devops = &(phydev->ops); memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = off; devops->len = phydev->writesize; devops->oobbuf = NULL; devops->datbuf = (u8 *)addr; devops->mode = NAND_SOFT_ECC; amlnand_dump_page(phydev); return 0; } if ((strcmp(cmd, "scrub") == 0) || (strcmp(cmd, "erase") == 0)) { int scrub_flag = !strncmp(cmd, "scrub", 5); if (argc < 2) goto usage; if (scrub_flag) { puts("Warning:" "devscrub option will erase all factory set "\ "bad blocks !\n"\ " "\ "There is no reliable way to recover them.\n"\ " "\ "Use this command only for testing purposes "\ "if you\n"\ " "\ "are sure of what you are doing !\n" "\nReally scrub this NAND flash ? < y/N >"\ "\n"); scrub_flag = 0; if (nand_protect) { if (getc() == 'y') { puts("y"); if (getc() == '\r') scrub_flag = 1; else { puts("scrub aborted\n"); return -1; } } else { puts("scrub aborted\n"); return -1; } } else scrub_flag = 1; } if (!strcmp(argv[2], "whole")) { off = 0; /* ((u64)flash->chipsize << 20); */ size = chipsize; erase_addr = erase_off = off; erase_len = size; printf("whole dev.\n"); } else { if ((arg_off_size(argc - 2, (char **)(argv + 2), chipsize, &off, &size) != 0)) goto usage; erase_addr = erase_off = off; erase_len = size; } erase_addr = erase_off = off; erase_len = size; ret = amlnf_erase_ops(off, erase_len, scrub_flag); if (ret < 0) aml_nand_msg("nand erase failed"); return ret; } if (strcmp(cmd, "markbad") == 0) { if (argc < 4) { goto usage; } dev_name = argv[2]; if (strcmp(dev_name, "boot") == 0) { dev_name = NAND_BOOT_NAME; } else if(strcmp(dev_name, "code") == 0){ dev_name = NAND_CODE_NAME; }else if(strcmp(dev_name, "cache") == 0){ dev_name = NAND_CACHE_NAME; }else if(strcmp(dev_name, "data") == 0){ dev_name = NAND_DATA_NAME; }else{ aml_nand_msg("input wrong name!! %s",dev_name); goto usage; } phydev = aml_phy_get_dev(dev_name); if (!phydev) { aml_nand_msg("phydev be NULL"); return -1; } devops = &(phydev->ops); if ((arg_off_size(argc - 3, (char **)(argv + 3), phydev->size, &off, &size) != 0)) { goto usage; } aml_nand_dbg("off:0x%llx size:%llx.\n", off, size); erase_addr =erase_off= off; erase_len = size; aml_nand_dbg("erase_len = %llx",erase_len); aml_nand_dbg("erase_off = %llx",erase_off); if (erase_len < phydev->erasesize) { printf("Warning: markbad size 0x%08x smaller than one " \ "block 0x%08x\n",(unsigned int)erase_len, phydev->erasesize); printf(" markbad 0x%08x instead\n", phydev->erasesize); erase_len = phydev->erasesize; } for (; erase_addr erasesize) { memset(devops, 0x0, sizeof(struct phydev_ops)); devops->addr = erase_addr; devops->len = phydev->erasesize; devops->mode = NAND_HW_ECC; ret = phydev->block_isbad(phydev); if (ret > 0) { printf("\rSkipping bad block at 0x%08llx\n", erase_addr); continue; } else if (ret < 0) { printf("\n:AMLNAND get bad block failed: ret=%d at addr=%llx\n",ret, erase_addr); return -1; } ret = phydev->block_markbad(phydev); if (ret < 0) printf("AMLNAND bad block mark failed: %llx\n", erase_addr); } printf("NAND %s %s\n", "MARKBAD", (ret <0) ? "ERROR" : "OK"); return 0; } if (strcmp(cmd, "markbad_reserved") == 0) { if (argc < 3) { goto usage; } if (!(str2long(argv[2], (unsigned long*)&markbad_reserved_addr))) { aml_nand_dbg("'%s' is not a number", argv[2]); goto usage; } printf("mark_reserved block:%d\n", (int)markbad_reserved_addr); ret = amlnf_markbad_reserved_ops(markbad_reserved_addr); printf("NAND %s %s\n", "MARKBAD", (ret <0) ? "ERROR" : "OK"); return 0; } usage: printf("may be invalid argus, check again.\n"); cmd_usage(cmdtp); return 1; } #ifdef CONFIG_SYS_LONGHELP static char amlnand_help_text[] = "init - init amlnand_phy here\n" "chipinfo - show aml chip information\n" "device[dev] - show or set current device\n" "partition* [part] - show or set current partition\n" "plane[dev] - show or set current plane mode\n" "read - addr off|partition size\n" "write - addr off|partition size\n" " read/write 'size' bytes starting at offset 'off'\n" " to/from memory address 'addr', skipping bad blocks.\n" "erase[clean|whole][off size] - erase 'size' bytes from\n" " offset 'off' (entire device if not specified)\n" "dump addr off\n" " show the raw data to addr at offset off\n" "read_byte - addr off|partition size\n" "write_byte - addr off|partition size\n" " read_byte/write_byte 'size' bytes starting at offset 'off'\n" " to/from memory address 'addr', skipping bad blocks.\n" "devread - addr off|partition size\n" "devwrite - addr off|partition size\n" " read/write 'size' bytes starting at offset 'off' in device[dev]\n" " to/from memory address 'addr', skipping bad blocks.\n" "deverase[whole][off size] - erase 'size' bytes from\n" " offset 'off' (entire device if not specified) in device[dev]\n" "markbad addr - mark block bad at addr\n" "mark_reserved reserved_blk_NO -mark reserved_blk_NO bad \n" "ldevice[dev] - show/get nftl(logic) device by name\n" "rom_read/write addr off cnt - read/write uboot.\n" "boot - show boot flag" #if (AML_CFG_DTB_RSV_EN) "dtb_read/write addr cnt - read/write dtd.\n" "dtb_erase - erase dtb!\n" #endif #if (AML_CFG_KEY_RSV_EN) "key_read/write addr cnt - read/write dtd.\n" "key_erase - erase keys!\n" #endif ""; #endif U_BOOT_CMD( amlnf, CONFIG_SYS_MAXARGS, 0, do_amlnfphy, "aml nand sub-system", amlnand_help_text );