static int m1_nand_boot_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); uint8_t *oob_buf = chip->oob_poi; unsigned nand_page_size = M1_BOOT_WRITE_SIZE; unsigned pages_per_blk_shift = (chip->phys_erase_shift - chip->page_shift); int user_byte_num = (((nand_page_size + chip->ecc.size - 1) / chip->ecc.size) * aml_chip->user_byte_mode); int error = 0, i = 0, stat = 0; memset(buf, 0xff, (1 << chip->page_shift)); WARN_ON(!aml_chip->valid_chip[0]); if (aml_chip->valid_chip[i]) { if (!aml_chip->aml_nand_wait_devready(aml_chip, i)) { printk ("read couldn`t found selected chip: %d ready\n", i); error = -EBUSY; goto exit; } error = aml_chip->aml_nand_dma_read(aml_chip, buf, nand_page_size, aml_chip->bch_mode); if (error) goto exit; aml_chip->aml_nand_get_user_byte(aml_chip, oob_buf, user_byte_num); stat = aml_chip->aml_nand_hwecc_correct(aml_chip, buf, nand_page_size, oob_buf); if (stat < 0) { mtd->ecc_stats.failed++; printk("aml nand read data ecc failed at blk %d chip %d\n", (page >> pages_per_blk_shift), i); } else
/* * The legacy NAND code saved the environment in the first NAND device i.e., * nand_dev_desc + 0. This is also the behaviour using the new NAND code. */ int writeenv(size_t offset, u_char *buf) { struct mtd_info *mtd; struct env_oobinfo_t *env_oobinfo; int error = 0; size_t addr = 0; size_t amount_saved = 0; size_t len; struct mtd_oob_ops aml_oob_ops; unsigned char *data_buf; unsigned char env_oob_buf[sizeof(struct env_oobinfo_t)]; mtd = nand_info[nand_curr_device]; if (mtd == NULL) return 1; struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); data_buf = kzalloc(mtd->writesize, GFP_KERNEL); if (data_buf == NULL) return -ENOMEM; addr = offset; env_oobinfo = (struct env_oobinfo_t *)env_oob_buf; memcpy(env_oobinfo->name, ENV_NAND_MAGIC, 4); env_oobinfo->ec = aml_chip->aml_nandenv_info->env_valid_node->ec; env_oobinfo->timestamp = aml_chip->aml_nandenv_info->env_valid_node->timestamp; env_oobinfo->status_page = 1; while (amount_saved < CONFIG_ENV_SIZE ) { aml_oob_ops.mode = MTD_OOB_AUTO; aml_oob_ops.len = mtd->writesize; aml_oob_ops.ooblen = sizeof(struct env_oobinfo_t); aml_oob_ops.ooboffs = mtd->ecclayout->oobfree[0].offset; aml_oob_ops.datbuf = data_buf; aml_oob_ops.oobbuf = env_oob_buf; memset((unsigned char *)aml_oob_ops.datbuf, 0x0, mtd->writesize); len = min(mtd->writesize, CONFIG_ENV_SIZE - amount_saved); memcpy((unsigned char *)aml_oob_ops.datbuf, buf + amount_saved, len); error = mtd->write_oob(mtd, addr, &aml_oob_ops); if (error) { printf("blk check good but write failed: %llx, %d\n", offset, error); return 1; } addr += mtd->writesize;; amount_saved += mtd->writesize; } if (amount_saved < CONFIG_ENV_SIZE) return 1; kfree(data_buf); return 0; }
int saveenv(void) { struct mtd_info * mtd=get_mtd_device_nm(NAND_NORMAL_NAME); if (IS_ERR(mtd)) return 1; struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); size_t total; size_t offset = CONFIG_ENV_OFFSET; int ret = 0; nand_erase_options_t nand_erase_options; offset = (1024 * aml_chip->page_size * (mtd->writesize / (aml_chip->plane_num * aml_chip->page_size))); if (CONFIG_ENV_OFFSET < offset) _debug ("env define offset must larger than 1024 page size: %d \n", mtd->writesize); else offset = CONFIG_ENV_OFFSET; env_ptr->flags++; total = CONFIG_ENV_SIZE; nand_erase_options.length = CONFIG_ENV_RANGE; nand_erase_options.quiet = 0; nand_erase_options.jffs2 = 0; nand_erase_options.scrub = 0; if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE) return 1; if(gd->env_valid == 1) { puts ("Erasing redundant Nand...\n"); nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND; if (nand_erase_opts(mtd, &nand_erase_options)) return 1; puts ("Writing to redundant Nand... "); ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) env_ptr); } else { puts ("Erasing Nand...\n"); nand_erase_options.offset = CONFIG_ENV_OFFSET; if (nand_erase_opts(mtd, &nand_erase_options)) return 1; puts ("Writing to Nand... "); ret = writeenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); } if (ret) { puts("FAILED!\n"); return 1; } puts ("done\n"); gd->env_valid = (gd->env_valid == 2 ? 1 : 2); return ret; }
int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) { int i, dev, ret = 0; ulong addr; loff_t off, size; char *cmd, *s; nand_info_t *nand; #ifdef CONFIG_SYS_NAND_QUIET int quiet = CONFIG_SYS_NAND_QUIET; #else int quiet = 0; #endif const char *quiet_str = getenv("quiet"); #if ((defined CONFIG_AML_NAND_KEY) || (defined MX_REVD) || (defined CONFIG_SECURE_NAND)) int chip_num , tmp_chip_num, error; nand = nand_info[nand_curr_device]; struct mtd_info *mtd =nand; struct aml_nand_chip *aml_chip = mtd_to_nand_chip(nand); #endif /* at least two arguments please */ if (argc < 2) goto usage; if (quiet_str) quiet = simple_strtoul(quiet_str, NULL, 0) != 0; cmd = argv[1]; #ifdef CONFIG_AML_NAND_KEY if (strcmp(cmd, "key") == 0){ aml_chip->key_protect = 1; //force nand key can be erased return 0; } #endif #ifdef CONFIG_SECURE_NAND if (strcmp(cmd, "secure") == 0){ aml_chip->secure_protect = 1; //force nand key can be erased return 0; } #endif #ifdef CONFIG_SECURE_NAND if (strcmp(cmd, "secure") == 0){ aml_chip->secure_protect = 1; //force nand key can be erased return 0; } #endif if(strcmp(cmd, "exist") == 0){ if(nand_info[1]){ printf("nand exist return 0\n"); return 0; } else{ printf("nand exist return 1\n"); return 1; } } #ifdef MX_REVD if (strcmp(cmd, "errstat") == 0){ printk("checking chiprev here\n"); if(aml_chip->err_sts == NAND_CHIP_REVB_HY_ERR){ printk("Must use RevD chip for Hynix 26nm/20nm nand boot without SPI!!!\n"); return NAND_CHIP_REVB_HY_ERR; } return 0; } #endif #ifdef CONFIG_SECURE_NAND if (strcmp(cmd, "secure") == 0){ aml_chip->secure_protect = 1; //force nand key can be erased return 0; } #endif if (strcmp(cmd, "info") == 0) { #ifdef CONFIG_AML_NAND_KEY aml_chip->key_protect = 0; //protect nand key can not be erased #endif #ifdef CONFIG_SECURE_NAND aml_chip->secure_protect = 0; //protect nand secure can not be erased #endif putc('\n'); for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { nand = nand_info[i]; if (!nand) { nand_init(); if (!nand) return -1; } if (nand->name) nand_print_info(i); } return 0; } if (strcmp(cmd, "init") == 0) { nand_init(); return 0; } //cmd for nand test , if nand is ok , then trigger power off if (strcmp(cmd, "test") == 0) { int ret=-1; puts("\ntest the nand flash ***\n"); for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) { nand = nand_info[i]; if (!nand) { ret=nand_test_init(); printf("\n***nand_test_init()in NAND DEVICE %d returned:%d***\n ", i,ret); if (ret) return -1; } } return 0; } if (strcmp(cmd, "scrub_detect") == 0) { if (nand_curr_device < 0 || nand_curr_device >= (CONFIG_SYS_MAX_NAND_DEVICE+2)) { puts("\nno devices available\n"); return 1; } nand = nand_info[nand_curr_device]; aml_nand_stupid_dectect_badblock(nand); return 0; } if (strcmp(cmd, "device") == 0) { if (argc < 3) { putc('\n'); if ((nand_curr_device < 0) || (nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE)) puts("no devices available\n"); else nand_print_info(nand_curr_device); return 0; } dev = (int)simple_strtoul(argv[2], NULL, 10); if (dev < 0 || dev >= (CONFIG_SYS_MAX_NAND_DEVICE+1) || !nand_info[dev]->name) { puts("No such device\n"); return 1; } printf("Device %d: %s", dev, nand_info[dev]->name); puts("... is now current device\n"); nand_curr_device = dev; #ifdef CONFIG_SYS_NAND_SELECT_DEVICE /* * Select the chip in the board/cpu specific driver */ board_nand_select_device(nand_info[dev]->priv, dev); #endif return 0; } if (strcmp(cmd, "bad") != 0 && strcmp(cmd, "erase") != 0 && strncmp(cmd, "dump", 4) != 0 && strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && strcmp(cmd, "biterr") != 0 && strncmp(cmd, "rom_protect", 11) != 0 && strncmp(cmd, "wr_rd_cmp", 9) != 0 && strncmp(cmd, "rom_write", 9) != 0 && (strncmp(cmd, "rom_read", 8) != 0) && strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 && strcmp(cmd, "factory_info") != 0 && strcmp(cmd, "show_para_page")&& strncmp(cmd, "scrub_safe", 10) != 0) //my_ goto usage; /* the following commands operate on the current device */ if (nand_curr_device < 0 || nand_curr_device >= (CONFIG_SYS_MAX_NAND_DEVICE+2)) { puts("\nno devices available\n"); return 1; } nand = nand_info[nand_curr_device]; if (!nand) return -1; if (strcmp(cmd, "bad") == 0) { printf("\nDevice %d bad blocks:\n", nand_curr_device); for (off = 0; off < nand->size; off += nand->erasesize) if (nand_block_isbad(nand, off)) printf(" %09llx\n", off); return 0; } /* * Syntax is: * 0 1 2 3 4 * nand erase [clean] [off size] */ if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0 || strcmp(cmd, "scrub_safe") == 0) { nand_erase_options_t opts; int argc_cnt = 2; //printk("%s\n", argv[2]); /* if (isstring(argv[2])) { nand = get_mtd_device_nm(argv[2]); if (IS_ERR(nand)){ printf("get nand device err\n"); return 1; } argc_cnt++; } */ /* "clean" at index 2 means request to write cleanmarker */ int clean = argc > argc_cnt && !strcmp("clean", argv[argc_cnt]); if (clean) argc_cnt++; int o = argc_cnt; int scrub = !strncmp(cmd, "scrub",10); int scrub_safe = !strncmp(cmd, "scrub_safe",10); if(scrub_safe) printf("\nNAND %s: ", scrub_safe ? "scrub_safe" : "erase"); else printf("\nNAND %s: ", scrub ? "scrub" : "erase"); if (argv[argc_cnt]) { if(!strcmp(argv[argc_cnt], "whole")) { off = 0; size = nand->size; printf("whole chip.\n"); } } else { /* skip first two or three arguments, look for offset and size */ if ((strcmp(cmd, "erase") == 0) && (argc < 3)) { goto usage; } if ((arg_off_size(argc - o, argv + o, nand, &off, &size) != 0)) { return 1; } } memset(&opts, 0, sizeof(opts)); opts.offset = off; opts.length = size; opts.jffs2 = clean; opts.quiet = quiet; if (scrub) { puts("Warning: " "scrub 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"); if(nand_protect) { if (getc() == 'y') { puts("y"); if (getc() == '\r') opts.scrub = 1; else { puts("scrub aborted\n"); return -1; } } else { puts("scrub aborted\n"); return -1; } } else { opts.scrub = 1; } } else if(scrub_safe){ puts("Warning: " "scrub_safe option will erase all " "bad blocks except factory bad blocks!\n"); opts.scrub = 2; // indicate scrub_safe } ret = nand_erase_opts(nand, &opts); printf("%s\n", ret ? "ERROR" : "OK"); #ifdef CONFIG_AML_NAND_KEY aml_chip->key_protect = 0; //protect nand key can not be erased #endif #ifdef CONFIG_SECURE_NAND aml_chip->secure_protect = 0; //protect nand secure can not be erased #endif return ret == 0 ? 0 : 1; } if (strncmp(cmd, "dump", 4) == 0) { if (argc < 3) goto usage; s = strchr(cmd, '.'); //off = (loff_t)simple_strtoul(argv[2], NULL, 16); if (!(str2longlong(argv[2], (unsigned long long*)(&off)))) return -1; if (s != NULL && strcmp(s, ".oob") == 0) ret = nand_dump(nand, off, 1); else ret = nand_dump(nand, off, 0); return ret == 0 ? 1 : 0; } if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { int read; if (argc < 4) goto usage; if (isstring(argv[2])) { nand = get_mtd_device_nm(argv[2]); if (IS_ERR(nand)) goto usage; addr = (ulong)simple_strtoul(argv[3], NULL, 16); read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ printf("\nNAND %s: %s ", read ? "read" : "write", argv[2]); if (argc == 4) { extern unsigned int get_mtd_size(char *name); off = 0; size = get_mtd_size(argv[2]); } else { if (arg_off_size(argc - 4, argv + 4, nand, &off, &size) != 0) return 1; } } else { addr = (ulong)simple_strtoul(argv[2], NULL, 16); read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ printf("\nNAND %s: ", read ? "read" : "write"); if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0) return 1; } #ifdef CONFIG_AMLROM_NANDBOOT if((read==0) && ((off)<(1024* nand->writesize)) && (nand_curr_device == 0)){ printf("offset 0x%llx in aml-boot area ,abort\n", off); return -1; } #endif s = strchr(cmd, '.'); if (!s || !strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i")) { if (read) ret = nand_read_skip_bad(nand, off, &size, (u_char *)addr, 0); else ret = nand_write_skip_bad(nand, off, &size, (u_char *)addr, 0); } else if (!strcmp(s, ".oob")) { /* out-of-band data */ mtd_oob_ops_t ops = { .oobbuf = (u8 *)addr, .ooblen = size, .mode = MTD_OOB_RAW }; if (read) ret = nand->read_oob(nand, off, &ops); else ret = nand->write_oob(nand, off, &ops); } else if (!strcmp(s, ".raw")) {
int readenv (size_t offset, u_char * buf) { struct mtd_info *mtd; struct env_oobinfo_t *env_oobinfo; struct aml_nand_bbt_info *nand_bbt_info; int error = 0, start_blk, total_blk, i, j; size_t addr = 0; size_t amount_loaded = 0; size_t len; struct mtd_oob_ops aml_oob_ops; unsigned char *data_buf; unsigned char env_oob_buf[sizeof(struct env_oobinfo_t)]; mtd = nand_info[nand_curr_device]; if (mtd == NULL) return 1; struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); if (!aml_chip->aml_nandenv_info->env_valid) return 2; addr = (1024 * mtd->writesize / aml_chip->plane_num); start_blk = addr / mtd->erasesize; total_blk = mtd->size / mtd->erasesize; addr = aml_chip->aml_nandenv_info->env_valid_node->phy_blk_addr; addr *= mtd->erasesize; addr += aml_chip->aml_nandenv_info->env_valid_node->phy_page_addr * mtd->writesize; data_buf = kzalloc(mtd->writesize, GFP_KERNEL); if (data_buf == NULL) return -ENOMEM; env_oobinfo = (struct env_oobinfo_t *)env_oob_buf; while (amount_loaded < CONFIG_ENV_SIZE ) { aml_oob_ops.mode = MTD_OOB_AUTO; aml_oob_ops.len = mtd->writesize; aml_oob_ops.ooblen = sizeof(struct env_oobinfo_t); aml_oob_ops.ooboffs = mtd->ecclayout->oobfree[0].offset; aml_oob_ops.datbuf = data_buf; aml_oob_ops.oobbuf = env_oob_buf; memset((unsigned char *)aml_oob_ops.datbuf, 0x0, mtd->writesize); memset((unsigned char *)aml_oob_ops.oobbuf, 0x0, aml_oob_ops.ooblen); error = mtd->read_oob(mtd, addr, &aml_oob_ops); if ((error != 0) && (error != -EUCLEAN)) { printf("blk check good but read failed: %llx, %d\n", (uint64_t)addr, error); return 1; } if (memcmp(env_oobinfo->name, ENV_NAND_MAGIC, 4)) printf("invalid nand env magic: %llx\n", (uint64_t)offset); addr += mtd->writesize; len = min(mtd->writesize, CONFIG_ENV_SIZE - amount_loaded); memcpy(buf + amount_loaded, data_buf, len); amount_loaded += mtd->writesize; } if (amount_loaded < CONFIG_ENV_SIZE) return 1; nand_bbt_info = (struct aml_nand_bbt_info *)(env_ptr->data + default_environment_size); if ((!memcmp(nand_bbt_info->bbt_head_magic, BBT_HEAD_MAGIC, 4)) && (!memcmp(nand_bbt_info->bbt_tail_magic, BBT_TAIL_MAGIC, 4))) { for (i=start_blk; i<total_blk; i++) { aml_chip->block_status[i] = NAND_BLOCK_GOOD; for (j=0; j<MAX_BAD_BLK_NUM; j++) { if (nand_bbt_info->nand_bbt[j] == i) { aml_chip->block_status[i] = NAND_BLOCK_BAD; break; } } } memcpy((unsigned char *)aml_chip->aml_nandenv_info->nand_bbt_info.bbt_head_magic, (unsigned char *)nand_bbt_info, sizeof(struct aml_nand_bbt_info)); } kfree(data_buf); return 0; }
/* * The legacy NAND code saved the environment in the first NAND device i.e., * nand_dev_desc + 0. This is also the behaviour using the new NAND code. */ int writeenv(size_t offset, u_char *buf) { struct mtd_info *mtd; struct env_oobinfo_t *env_oobinfo; int error = 0; size_t addr = 0; size_t amount_saved = 0; size_t len; struct mtd_oob_ops aml_oob_ops; unsigned char *data_buf; unsigned char env_oob_buf[sizeof(struct env_oobinfo_t)]; mtd = nand_info[nand_curr_device]; if (mtd == NULL) return 1; struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); data_buf = kzalloc(mtd->writesize, GFP_KERNEL); if (data_buf == NULL) return -ENOMEM; addr = offset; env_oobinfo = (struct env_oobinfo_t *)env_oob_buf; memcpy(env_oobinfo->name, ENV_NAND_MAGIC, 4); env_oobinfo->ec = aml_chip->aml_nandenv_info->env_valid_node->ec; env_oobinfo->timestamp = aml_chip->aml_nandenv_info->env_valid_node->timestamp; env_oobinfo->status_page = 1; while (amount_saved < CONFIG_ENV_SIZE ) { aml_oob_ops.mode = MTD_OOB_AUTO; aml_oob_ops.len = mtd->writesize; aml_oob_ops.ooblen = sizeof(struct env_oobinfo_t); aml_oob_ops.ooboffs = mtd->ecclayout->oobfree[0].offset; aml_oob_ops.datbuf = data_buf; aml_oob_ops.oobbuf = env_oob_buf; memset((unsigned char *)aml_oob_ops.datbuf, 0x0, mtd->writesize); len = min(mtd->writesize, CONFIG_ENV_SIZE - amount_saved); memcpy((unsigned char *)aml_oob_ops.datbuf, buf + amount_saved, len); error = mtd->write_oob(mtd, addr, &aml_oob_ops); if (error) { printf("blk check good but write failed: %llx, %d\n", offset, error); return 1; } addr += mtd->writesize;; amount_saved += mtd->writesize; } if (amount_saved < CONFIG_ENV_SIZE) return 1; kfree(data_buf); return 0; } #ifdef CONFIG_ENV_OFFSET_REDUND int saveenv(void) { struct mtd_info * mtd=get_mtd_device_nm(NAND_NORMAL_NAME); if (IS_ERR(mtd)) return 1; struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); size_t total; size_t offset = CONFIG_ENV_OFFSET; int ret = 0; nand_erase_options_t nand_erase_options; offset = (1024 * aml_chip->page_size * (mtd->writesize / (aml_chip->plane_num * aml_chip->page_size))); if (CONFIG_ENV_OFFSET < offset) _debug ("env define offset must larger than 1024 page size: %d \n", mtd->writesize); else offset = CONFIG_ENV_OFFSET; env_ptr->flags++; total = CONFIG_ENV_SIZE; nand_erase_options.length = CONFIG_ENV_RANGE; nand_erase_options.quiet = 0; nand_erase_options.jffs2 = 0; nand_erase_options.scrub = 0; if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE) return 1; if(gd->env_valid == 1) { puts ("Erasing redundant Nand...\n"); nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND; if (nand_erase_opts(mtd, &nand_erase_options)) return 1; puts ("Writing to redundant Nand... "); ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) env_ptr); } else { puts ("Erasing Nand...\n"); nand_erase_options.offset = CONFIG_ENV_OFFSET; if (nand_erase_opts(mtd, &nand_erase_options)) return 1; puts ("Writing to Nand... "); ret = writeenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); } if (ret) { puts("FAILED!\n"); return 1; } puts ("done\n"); gd->env_valid = (gd->env_valid == 2 ? 1 : 2); return ret; } #else /* ! CONFIG_ENV_OFFSET_REDUND */ int saveenv(void) { struct mtd_info *mtd; struct aml_nand_bbt_info *nand_bbt_info; struct env_free_node_t *env_free_node, *env_tmp_node; int error = 0, pages_per_blk, i = 1; size_t addr = 0; struct erase_info aml_env_erase_info; mtd = nand_info[nand_curr_device]; if (mtd == NULL) return 1; struct aml_nand_chip *aml_chip = mtd_to_nand_chip(mtd); if (!aml_chip->aml_nandenv_info->env_init) return 1; pages_per_blk = mtd->erasesize / mtd->writesize; if ((mtd->writesize < CONFIG_ENV_SIZE) && (aml_chip->aml_nandenv_info->env_valid == 1)) i = (CONFIG_ENV_SIZE + mtd->writesize - 1) / mtd->writesize; if (aml_chip->aml_nandenv_info->env_valid) { aml_chip->aml_nandenv_info->env_valid_node->phy_page_addr += i; if ((aml_chip->aml_nandenv_info->env_valid_node->phy_page_addr + i) > pages_per_blk) { env_free_node = kzalloc(sizeof(struct env_free_node_t), GFP_KERNEL); if (env_free_node == NULL) return -ENOMEM; env_free_node->phy_blk_addr = aml_chip->aml_nandenv_info->env_valid_node->phy_blk_addr; env_free_node->ec = aml_chip->aml_nandenv_info->env_valid_node->ec; env_tmp_node = aml_chip->aml_nandenv_info->env_free_node; while (env_tmp_node->next != NULL) { env_tmp_node = env_tmp_node->next; } env_tmp_node->next = env_free_node; env_tmp_node = aml_chip->aml_nandenv_info->env_free_node; aml_chip->aml_nandenv_info->env_valid_node->phy_blk_addr = env_tmp_node->phy_blk_addr; aml_chip->aml_nandenv_info->env_valid_node->phy_page_addr = 0; aml_chip->aml_nandenv_info->env_valid_node->ec = env_tmp_node->ec; aml_chip->aml_nandenv_info->env_valid_node->timestamp += 1; aml_chip->aml_nandenv_info->env_free_node = env_tmp_node->next; kfree(env_tmp_node); } } else { env_tmp_node = aml_chip->aml_nandenv_info->env_free_node; aml_chip->aml_nandenv_info->env_valid_node->phy_blk_addr = env_tmp_node->phy_blk_addr; aml_chip->aml_nandenv_info->env_valid_node->phy_page_addr = 0; aml_chip->aml_nandenv_info->env_valid_node->ec = env_tmp_node->ec; aml_chip->aml_nandenv_info->env_valid_node->timestamp += 1; aml_chip->aml_nandenv_info->env_free_node = env_tmp_node->next; kfree(env_tmp_node); } addr = aml_chip->aml_nandenv_info->env_valid_node->phy_blk_addr; addr *= mtd->erasesize; addr += aml_chip->aml_nandenv_info->env_valid_node->phy_page_addr * mtd->writesize; if (aml_chip->aml_nandenv_info->env_valid_node->phy_page_addr == 0) { memset(&aml_env_erase_info, 0, sizeof(struct erase_info)); aml_env_erase_info.mtd = mtd; aml_env_erase_info.addr = addr; aml_env_erase_info.len = mtd->erasesize; error = mtd->erase(mtd, &aml_env_erase_info); if (error) { printf("env free blk erase failed %d\n", error); mtd->block_markbad(mtd, addr); return error; } aml_chip->aml_nandenv_info->env_valid_node->ec++; } nand_bbt_info = &aml_chip->aml_nandenv_info->nand_bbt_info; if ((!memcmp(nand_bbt_info->bbt_head_magic, BBT_HEAD_MAGIC, 4)) && (!memcmp(nand_bbt_info->bbt_tail_magic, BBT_TAIL_MAGIC, 4))) { memcpy(env_ptr->data + default_environment_size, aml_chip->aml_nandenv_info->nand_bbt_info.bbt_head_magic, sizeof(struct aml_nand_bbt_info)); env_crc_update (); } printf("Writing to Nand... \n"); if (writeenv(addr, (u_char *) env_ptr)) { printf("FAILED!\n"); return 1; } printf("Successful!\n"); return error; }