loaderu-boot-2011.12driversmtdnandnand_base.c
可以从mtd的结构体初始化关联出来,每一个mtd设备关联chip指针,最终调用flash硬件接口
mtd->read = nand_erase; -> nand_erase_nand(mtd, instr, 0); -> chip->erase_cmd(mtd, page & chip->pagemask);
loaderu-boot-2011.12driversmtdnandnand_util.c
int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
{
struct jffs2_unknown_node cleanmarker;
erase_info_t erase;
unsigned long erase_length, erased_length;
int bbtest = 1;
int result;
int percent_complete = -1;
const char *mtd_device = meminfo->name;
struct mtd_oob_ops oob_opts;
struct nand_chip *chip = meminfo->priv;
if ((opts->offset & (meminfo->writesize - 1)) != 0) {
printf("Attempt to erase non page aligned datan");
return -1;
}
memset(&erase, 0, sizeof(erase));
memset(&oob_opts, 0, sizeof(oob_opts));
erase.mtd = meminfo;
erase.len = meminfo->erasesize;//64*2048
erase.addr = opts->offset; //0x0
erase_length = lldiv(opts->length + meminfo->erasesize - 1,
meminfo->erasesize); (0xe0000 + 64*2048 - 1) / (64*2048)
cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
cleanmarker.totlen = cpu_to_je32(8);
if (opts->scrub) {
erase.scrub = opts->scrub;
if (chip->bbt) {
kfree(chip->bbt);
}
chip->bbt = NULL;
}
for (erased_length = 0;
erased_length < erase_length;
erase.addr += meminfo->erasesize) {
WATCHDOG_RESET ();
if (!opts->scrub && bbtest) {
int ret = meminfo->block_isbad(meminfo, erase.addr);
if (ret > 0) {
if (!opts->quiet)
printf("rSkipping bad block at "
"0x%08llx "
" n",
erase.addr);
if (!opts->spread)
erased_length++;
continue;
} else if (ret < 0) {
printf("n%s: MTD get bad block failed: %dn",
mtd_device,
ret);
return -1;
}
}
erased_length++;
result = meminfo->erase(meminfo, &erase);
if (result != 0) {
printf("n%s: MTD Erase failure: %dn",
mtd_device, result);
continue;
}
if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) {
chip->ops.ooblen = 8;
chip->ops.datbuf = NULL;
chip->ops.oobbuf = (uint8_t *)&cleanmarker;
chip->ops.ooboffs = 0;
chip->ops.mode = MTD_OOB_AUTO;
result = meminfo->write_oob(meminfo,
erase.addr,
&chip->ops);
if (result != 0) {
printf("n%s: MTD writeoob failure: %dn",
mtd_device, result);
continue;
}
}
if (!opts->quiet) {
unsigned long long n = erased_length * 100ULL;
int percent;
do_div(n, erase_length);
percent = (int)n;
if (percent != percent_complete) {
percent_complete = percent;
printf("rErasing at 0x%llx -- %3d%% complete.",
erase.addr, percent);
if (opts->jffs2 && result == 0)
printf(" Cleanmarker written at 0x%llx.",
erase.addr);
}
}
}
if (!opts->quiet)
printf("n");
if (opts->scrub)
chip->scan_bbt(meminfo);
return 0;
}
int do_spi_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
{
int ret = 0;
ulong addr;
loff_t off, size;
char *cmd, *s;
nand_info_t *nand = (nand_info_t *)get_mtd_info();
#ifdef CONFIG_SYS_NAND_QUIET
int quiet = CONFIG_SYS_NAND_QUIET;
#else
int quiet = 0;
#endif
const char *quiet_str = getenv("quiet");
int dev = nand_curr_device;
int repeat = flag & CMD_FLAG_REPEAT;
if (argc<2)
goto usage;
if (quiet_str)
quiet = simple_strtoul(quiet_str, NULL, 0) != 0;
cmd = argv[1];
if (repeat && strcmp(cmd, "dump"))
return 0;
if (strcmp(cmd, "info") == 0) {
spi_nand_info();
return 0;
}
if (strcmp(cmd, "device") == 0) {
if (argc < 3) {
putc('n');
if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE)
puts("no devices availablen");
else
nand_print_and_set_info(dev);
return 0;
}
dev = (int)simple_strtoul(argv[2], NULL, 10);
set_dev(dev);
return 0;
}
if (strcmp(cmd, "bad") == 0) {
printf("nDevice %d bad blocks:n", dev);
for (off = 0; off < nand->size; off += nand->erasesize){
if (nand_block_isbad(nand, off))
printf(" %08llxn", (unsigned long long)off);
}
printf("NAND size is %08llx, Erase size is %08xn",nand->size, nand->erasesize);
return 0;
}
if (strncmp(cmd, "erase", 5) == 0 || strncmp(cmd, "scrub", 5) == 0) {
nand_erase_options_t opts;
int clean = argc > 2 && !strcmp("clean", argv[2]); //0
int scrub_yes = argc > 2 && !strcmp("-y", argv[2]);//0
int o = (clean || scrub_yes) ? 3 : 2; //2
int scrub = !strncmp(cmd, "scrub", 5);//0
int spread = 0;
int args = 2;
const char *scrub_warn =
"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 youn"
" "
"are sure of what you are doing!n"
"nReally scrub this NAND flash? n";
if (cmd[5] != 0) {
if (!strcmp(&cmd[5], ".spread")) {
spread = 1;
} else if (!strcmp(&cmd[5], ".part")) {
args = 1;
} else if (!strcmp(&cmd[5], ".chip")) {
args = 0;
} else {
goto usage;
}
}
if (argc != o + args)
goto usage;
#if 0
static inline int str2off(const char *p, loff_t *num)
{
char *endptr;
*num = simple_strtoull(p, &endptr, 16);
return *p != ' ' && *endptr == ' ';
}
static int arg_off(const char *arg, int *idx, loff_t *off, loff_t *maxsize)
{
nand_info_t *nand = (nand_info_t *)get_mtd_info();
if (!str2off(arg, off))
return get_part(arg, idx, off, maxsize);
if (*off >= nand->size) {
puts("Offset exceeds device limitn");
return -1;
}
*maxsize = nand->size - *off;
return 0;
}
// spi_nand erase 0x0 0xe0000
//arg_off_size(argc - o, argv + o, &dev, &off, &size)
static int arg_off_size(int argc-2, char *const argv[], int *idx,
loff_t *off, loff_t *size)
{
nand_info_t *nand = (nand_info_t *)get_mtd_info();
int ret;
loff_t maxsize = 0;
if (argc == 0) {
*off = 0;
*size = nand->size;
goto print;
}
ret = arg_off(argv[0], idx, off, &maxsize);
if (ret)
return ret;
if (argc == 1) {
*size = maxsize;
goto print;
}
if (!str2off(argv[1], size)) { //size=0xe0000
printf("'%s' is not a numbern", argv[1]);
return -1;
}
if (*size > maxsize) {
puts("Size exceeds partition or device limitn");
return -1;
}
print:
printf("device %d ", *idx);
if (*size == nand->size)
puts("whole chipn");
else
printf("offset 0x%llx, size 0x%llxn",
(unsigned long long)*off, (unsigned long long)*size);
return 0;
}
#endif
printf("nNAND %s: ", cmd);
if (arg_off_size(argc - o, argv + o, &dev, &off, &size) != 0)
return 1;
//nand = &nand_info[dev];
memset(&opts, 0, sizeof(opts));
opts.offset = off; //0x0
opts.length = size; //0xe0000
opts.jffs2 = clean;//0
opts.quiet = quiet;//0
opts.spread = spread;//0
if (scrub)) {
if (!scrub_yes)
puts(scrub_warn);
if (scrub_yes)
opts.scrub = 1;
else if (getc() == 'y') {
puts("y");
if (getc() == 'r')
opts.scrub = 1;
else {
puts("scrub abortedn");
return -1;
}
} else {
puts("scrub abortedn");
return -1;
}
}
ret = nand_erase_opts(nand, &opts);
printf("%sn", ret ? "ERROR" : "OK");
return ret == 0 ? 0 : 1;
}
if (strncmp(cmd, "dump", 4) == 0) {
if (argc < 3)
goto usage;
off = (int)simple_strtoul(argv[2], NULL, 16);
ret = nand_dump(nand, off, !strcmp(&cmd[4], ".oob"), repeat);
return ret == 0 ? 1 : 0;
}
if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
size_t rwsize;
ulong pagecount = 1;
int read;
int raw = 0;
int no_verify = 0;
if (argc < 4)
goto usage;
addr = (ulong)simple_strtoul(argv[2], NULL, 16);
read = strncmp(cmd, "read", 4) == 0;
printf("nNAND %s: ", read ? "read" : "write");
s = strchr(cmd, '.');
if (s && !strncmp(s, ".raw", 4)) {
char *_s = s;
int raw_size = 0;
uint32_t raw_unit_size = nand->writesize + nand->oobsize;
raw = 1;
while(_s) {
_s = strchr(_s+1, '.');
if (_s && !strncmp(_s, ".noverify", 9)) {
no_verify = 1;
} else if (_s && !strncmp(_s, ".size", 5)) {
raw_size = 1;
}
}
if (arg_off(argv[3], &dev, &off, &size))
return 1;
if (set_dev(dev))
return 1;
if (argc > 4 && !str2long(argv[4], &pagecount)) {
printf("'%s' is not a numbern", argv[4]);
return 1;
}
if (raw_size) {
pagecount = (pagecount+(raw_unit_size-1))/raw_unit_size;
printf("transfer size to %ld page(s),", pagecount);
}
if (pagecount * nand->writesize > size) {
puts("Size exceeds partition or device limitn");
return -1;
}
rwsize = pagecount * (raw_unit_size);
} else {
if (arg_off_size(argc - 3, argv + 3, &dev, &off,
&size) != 0)
return 1;
if (set_dev(dev))
return 1;
if (argc < 5)
adjust_size_for_badblocks(&size, off, dev);
rwsize = size;
}
if (!s || !strcmp(s, ".jffs2") ||
!strcmp(s, ".e") || !strcmp(s, ".i")) {
if (read)
ret = nand_read_skip_bad(nand, off, &rwsize,
(u_char *)addr);
else
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr, 0);
#ifdef CONFIG_CMD_NAND_TRIMFFS
} else if (!strcmp(s, ".trimffs")) {
if (read) {
printf("Unknown nand command suffix '%s'n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr,
WITH_DROp_FFS);
#endif
#ifdef CONFIG_CMD_NAND_YAFFS
} else if (!strcmp(s, ".yaffs")) {
if (read) {
printf("Unknown nand command suffix '%s'.n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr, WITH_YAFFS_OOB);
#endif
} else if (!strcmp(s, ".oob")) {
mtd_oob_ops_t ops = {
.oobbuf = (u8 *)addr,
.ooblen = rwsize,
.mode = MTD_OOB_RAW
};
if (read)
ret = nand->read_oob(nand, off, &ops);
else
ret = nand->write_oob(nand, off, &ops);
} else if (raw) {
ret = raw_access(nand, addr, off, pagecount, read,
no_verify);
} else {
printf("Unknown nand command suffix '%s'.n", s);
return 1;
}
printf(" %zu bytes %s: %sn", rwsize,
read ? "read" : "written", ret ? "ERROR" : "OK");
return ret == 0 ? 0 : 1;
}
if (strcmp(cmd, "markbad") == 0) {
argc -= 2;
argv += 2;
if (argc <= 0)
goto usage;
while (argc > 0) {
addr = simple_strtoul(*argv, NULL, 16);
if (nand->block_markbad(nand, addr)) {
printf("block 0x%08lx NOT marked "
"as bad! ERROR %dn",
addr, ret);
ret = 1;
} else {
printf("block 0x%08lx successfully "
"marked as badn",
addr);
}
--argc;
++argv;
}
return ret;
}
if (strcmp(cmd, "biterr") == 0) {
return 1;
}
usage:
return cmd_usage(cmdtp);
}
U_BOOT_CMD(
spi_nand, CONFIG_SYS_MAXARGS, 1, do_spi_nand,
"SPI-NAND sub-system",
"info - show available NAND devicesn"
"spi_nand device [dev] - show or set current devicen"
"spi_nand read - addr off|partition sizen"
"spi_nand write - addr off|partition sizen"
" read/write 'size' bytes starting at offset 'off'n"
" to/from memory address 'addr', skipping bad blocks.n"
"spi_nand read.raw - addr off|partition [count]n"
"spi_nand write.raw[.noverify] - addr off|partition [count]n"
" Use read.raw/write.raw to avoid ECC and access the flash as-is.n"
" 'count' is the page countn"
#ifdef CONFIG_CMD_NAND_TRIMFFS
"spi_nand write.trimffs - addr off|partition sizen"
" write 'size' bytes starting at offset 'off' from memory addressn"
" 'addr', skipping bad blocks and dropping any pages at the endn"
" of eraseblocks that contain only 0xFFn"
#endif
#ifdef CONFIG_CMD_NAND_YAFFS
"spi_nand write.yaffs - addr off|partition sizen"
" write 'size' bytes starting at offset 'off' with yaffs formatn"
" from memory address 'addr', skipping bad blocks.n"
#endif
"spi_nand erase[.spread] [clean] off size - erase 'size' bytes "
"from offset 'off'n"
" With '.spread', erase enough for given file size, otherwise,n"
" 'size' includes skipped bad blocks.n"
"spi_nand erase.part [clean] partition - erase entire mtd partition'n"
"spi_nand erase.chip [clean] - erase entire chip'n"
"spi_nand bad - show bad blocksn"
"spi_nand dump[.oob] off - dump pagen"
"spi_nand scrub [-y] off size | scrub.part partition | scrub.chipn"
" really clean NAND erasing bad blocks (UNSAFE)n"
"spi_nand markbad off [...] - mark bad block(s) at offset (UNSAFE)n"
"spi_nand biterr off - make a bit error at offset (UNSAFE)"
);
U_BOOT_CMD(
nand, CONFIG_SYS_MAXARGS, 1, do_spi_nand,
"NAND sub-system",
"alias spi_nand"
);
#测试结果-------擦除
Erasing at 0x20000 -- 14% complete.
Erasing at 0x40000 -- 28% complete.
Erasing at 0x60000 -- 42% complete.
Erasing at 0x80000 -- 57% complete.
Erasing at 0xa0000 -- 71% complete.
Erasing at 0xc0000 -- 85% complete.
Erasing at 0xe0000 -- 100% complete.
#include
#include
#define uint64_t unsigned long int
#define uint32_t unsigned int
#define int64_t long int
uint32_t __div64_32(uint64_t *n, uint32_t base)
{
uint64_t rem = *n;
uint64_t b = base;
uint64_t res, d = 1;
uint32_t high = rem >> 32;
res = 0;
if (high >= base) {
high /= base;
res = (uint64_t) high << 32;
rem -= (uint64_t) (high*base) << 32;
}
while ((int64_t)b > 0 && b < rem) {
b = b+b;
d = d+d;
}
do {
if (rem >= b) {
rem -= b;
res += d;
}
b >>= 1;
d >>= 1;
} while (d);
*n = res;
return rem;
}
# define do_div(n,base) ({
uint32_t __base = (base);
uint32_t __rem;
(void)(((typeof((n)) *)0) == ((unsigned long *)0));
if (((n) >> 32) == 0) {
__rem = (unsigned int)(n) % __base;
(n) = (unsigned int)(n) / __base;
} else
__rem = __div64_32(&(n), __base);
__rem;
})
static uint64_t lldiv(uint64_t dividend, uint32_t divisor)
{
uint64_t __res = dividend;
do_div(__res, divisor);
return(__res);
}
int main()
{
#if 0
char *endptr=NULL;
char *p="0x0";
printf("p:%s.n", p);
int num = strtoull(p, &endptr, 16);
printf("p:%s.n", p);
int value = ( *p != ' ' && *endptr == ' ');
char aa=' ';
printf("num:%d,value:%d,*p:%d,*endptr:%d,aa:%d.n", num, value, *p,*endptr,aa);
//p:0x0.
//p:0x0.
//num_value:0,value:1,*p:48,*endptr:0,aa:0.
unsigned long erase_length = lldiv(0xe0000+ 131072- 1,
131072);
printf("erase_length:%#x.n", erase_length);
unsigned long n = 700; int bbb=7;
int percent = do_div(n , bbb);
printf("percent:%d, n:%llu.n", percent, n);
#endif
int erased_length = 0;
int erase_length = 7 ;
int percent = 0;
int percent_complete = -1;
unsigned long n = 0;
long long erase_addr = 0;
for (erased_length = 0; erased_length < erase_length; )
{
erase_addr += 131072;
erased_length++;
#if 1
n = erased_length * 100ULL;
do_div(n, erase_length);
percent = (int)n;
#endif
if (percent != percent_complete)
{
percent_complete = percent;
printf("rErasing at 0x%llx -- %3d%% complete.",
erase_addr, percent);
}
}
return 0;
}