日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

u-boot的nand驱动写过程分析

發(fā)布時間:2025/3/20 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 u-boot的nand驱动写过程分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

從命令說起,在u-boot輸入下列命令:

nand write 40008000 0 20000
命令的意思是將內(nèi)存0x40008000開始的部分寫入nand,從nand地址0開始寫,寫入長度是0x200000

回車之后,代碼如何運行呢?命令的輸入,執(zhí)行之前都已經(jīng)分析過了,初始化過程也分析了

請參閱:

http://blog.csdn.net/andy_wsj/article/details/9335755

http://blog.csdn.net/andy_wsj/article/details/9339247

http://blog.csdn.net/andy_wsj/article/details/8614905


執(zhí)行這條命令,將調(diào)用\u-boot-sunxi-sunxi\common\cmd_nand.c內(nèi)的函數(shù)do_nand。

int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])


nand write 40008000 0 20000在參數(shù)argv中,而且
argv[0] = "nand"
argv[1] = "write"
argv[2] = "40008000"
argv[3] = "0"
argv[4] = "20000"
argc = 5 參數(shù)的個數(shù)


分析一下do_nand函數(shù)的片段,篇幅關系,只保留寫操作部分:
nt do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
{
int i, 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");
int dev = nand_curr_device; ? ? ? ? ? ? ? ? //當前NAND芯片,如果板上有多個芯片,則不能直接賦值,大部分板子都是一個NAND
int repeat = flag & CMD_FLAG_REPEAT;


/* at least two arguments please */
if (argc < 2)
goto usage;


if (quiet_str)
quiet = simple_strtoul(quiet_str, NULL, 0) != 0;


cmd = argv[1]; ? //cmd就指向命令“write”,


? ?........判斷是什么命令,多余判斷刪除了..............


/* The following commands operate on the current device, unless
* overridden by a partition specifier. ?Note that if somehow the
* current device is invalid, it will have to be changed to a valid
* one before these commands can run, even if a partition specifier
* for another device is to be used.
*/
if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE || ?//判斷芯片是否存在或是否定義
? ?!nand_info[dev].name) {
puts("\nno devices available\n");
return 1;
}
nand = &nand_info[dev]; ? //獲取定義的nand芯片信息
??
? ................
??
if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { ?//nand讀寫操作
size_t rwsize;
ulong pagecount = 1;
int read;
int raw;


if (argc < 4) ?
goto usage;


addr = (ulong)simple_strtoul(argv[2], NULL, 16); ?//將argv[2] = "40008000"轉(zhuǎn)換成16進制,0x40008000


read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */ ?//判斷讀寫操作類型
printf("\nNAND %s: ", read ? "read" : "write");


nand = &nand_info[dev];


s = strchr(cmd, '.'); ? //看看是否帶有擴展命令,如write.raw, write.jffs2等等,輸入是“write”,結(jié)果s = NULL;


if (s && !strcmp(s, ".raw")) {
? ? ? ......省略.....
? ? ??
} else { ?//執(zhí)行這里,計算地址偏移量,長度
if (arg_off_size(argc - 3, argv + 3, &dev,?
&off, &size) != 0)
return 1;


rwsize = size;
}


if (!s || !strcmp(s, ".jffs2") || ? ? ?//實際執(zhí)行這里
? ?!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, ? //執(zhí)行函數(shù)nand_write_skip_bad
?(u_char *)addr, 0);


} else if (......省略.....) {
......省略.....
......省略.....
} else {
printf("Unknown nand command suffix '%s'.\n", s);
return 1;
}


printf(" %zu bytes %s: %s\n", rwsize,
? ? ? read ? "read" : "written", ret ? "ERROR" : "OK");


return ret == 0 ? 0 : 1;
}


..........

return 0;
}
來看看函數(shù)nand_write_skip_bad,在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand_util.c內(nèi):
經(jīng)過do_nand處理,可知參數(shù)就是輸入命令的內(nèi)容:
offset ? 為 ?0
*length ?為 0x200000
buffer ? 指向0x40008000


int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
u_char *buffer, int flags)
{
int rval = 0, blocksize;
size_t left_to_write = *length;
u_char *p_buffer = buffer;
int need_skip;


#ifdef CONFIG_CMD_NAND_YAFFS
if (flags & WITH_YAFFS_OOB) {
if (flags & ~WITH_YAFFS_OOB)
return -EINVAL;


int pages;
pages = nand->erasesize / nand->writesize;
blocksize = (pages * nand->oobsize) + nand->erasesize;
if (*length % (nand->writesize + nand->oobsize)) {
printf ("Attempt to write incomplete page"
" in yaffs mode\n");
return -EINVAL;
}
} else
#endif
{
blocksize = nand->erasesize; ?//執(zhí)行這里,nand的刷新都是以塊為單位的,所以blocksize就是刷新的長度,對于cubieboard上的nand芯片,是1M+80K
}


/*
* nand_write() handles unaligned, partial page writes.
*
* We allow length to be unaligned, for convenience in
* using the $filesize variable.
*
* However, starting at an unaligned offset makes the
* semantics of bad block skipping ambiguous (really,
* you should only start a block skipping access at a
* partition boundary). ?So don't try to handle that.
*/
if ((offset & (nand->writesize - 1)) != 0) { ? ?//輸入的偏移量要以塊長度對齊
printf ("Attempt to write non page aligned data\n");
*length = 0;
return -EINVAL;
}


need_skip = check_skip_len(nand, offset, *length); ?//判斷是否需要越過壞塊,這里需要壞塊讀取操作,nand驅(qū)動的一個功能
if (need_skip < 0) {
printf ("Attempt to write outside the flash area\n");
*length = 0;
return -EINVAL;
}


if (!need_skip && !(flags & WITH_DROP_FFS)) { ? ? ? ?//不需要,即寫的部分沒有壞塊
rval = nand_write (nand, offset, length, buffer); ?//直接寫
if (rval == 0)
return 0;


*length = 0;
printf ("NAND write to offset %llx failed %d\n",
offset, rval);
return rval;
}


while (left_to_write > 0) { ?// 剩下要寫的字節(jié)數(shù),開始就是命令輸入的0x200000
size_t block_offset = offset & (nand->erasesize - 1);
size_t write_size, truncated_write_size;


WATCHDOG_RESET ();


if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { //從開始的位置往后找壞塊,直到找到一個可寫的為止
printf ("Skip bad block 0x%08llx\n",
offset & ~(nand->erasesize - 1));
offset += nand->erasesize - block_offset;
continue;
}


if (left_to_write < (blocksize - block_offset)) ?//找到可寫的塊,判斷寫入的數(shù)據(jù)是不是小于一塊,對于cubieboard,是1M
write_size = left_to_write; ? ? ? ? ? ? ? ? ? ?//由于輸入的是0x200000即2M,因此需要寫兩次
else
write_size = blocksize - block_offset;


#ifdef CONFIG_CMD_NAND_YAFFS
.......
#endif
{
truncated_write_size = write_size;
#ifdef CONFIG_CMD_NAND_TRIMFFS
?.......
#endif


rval = nand_write(nand, offset, &truncated_write_size, ?//調(diào)用nand_write,寫入數(shù)據(jù)
p_buffer);
offset += write_size; ? ? ? ? //偏移量往后移動
p_buffer += write_size; ? ? ? //數(shù)據(jù)指針往后移動
}


if (rval != 0) {
printf ("NAND write to offset %llx failed %d\n",
offset, rval);
*length -= left_to_write;
return rval;
}


left_to_write -= write_size; ? //剩下的字節(jié)數(shù),循環(huán)寫的條件
}


return 0;
}


無論如何寫,有沒有壞塊,最后都使用函數(shù)nand_write,接下來再看看這個函數(shù)
在文件在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand_base.c內(nèi):
這個函數(shù)就是寫的準備,這已經(jīng)執(zhí)行到驅(qū)動代碼的邏輯層了


static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
?size_t *retlen, const uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
int ret;


/* Do not allow writes past end of device */ 不能超過最大長度
if ((to + len) > mtd->size)
return -EINVAL;
if (!len)
return 0;


nand_get_device(chip, mtd, FL_WRITING); ?//獲取設備,就是獲取需要寫的那個nand芯片的數(shù)據(jù)


chip->ops.len = len; ? ? ? ? ? ? ? ? ? ? //寫入的長度,按輸入命令,第一次時這個就是一個塊的長度
chip->ops.datbuf = (uint8_t *)buf; ? ? ? //數(shù)據(jù)所在的位置,第一次就是輸入的內(nèi)存地址0x40008000處
chip->ops.oobbuf = NULL;


ret = nand_do_write_ops(mtd, to, &chip->ops); ? //執(zhí)行寫操作


*retlen = chip->ops.retlen;


nand_release_device(mtd);


return ret;
}
再看看nand_do_write_ops函數(shù),就在這個文件nand_base.c內(nèi),nand_write函數(shù)的上面:
到了這里,其實已經(jīng)接近硬件操作了,如果要寫一個nand驅(qū)動,實現(xiàn)寫操作,
看看這個函數(shù),就是知道需要實現(xiàn)的幾個操作了。下面對幾個關鍵的地方進行標記,說明寫驅(qū)動需要實現(xiàn)的功能
static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
? ? struct mtd_oob_ops *ops)
{
int chipnr, realpage, page, blockmask, column;
struct nand_chip *chip = mtd->priv;
uint32_t writelen = ops->len;


uint32_t oobwritelen = ops->ooblen;
uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ?
mtd->oobavail : mtd->oobsize;


uint8_t *oob = ops->oobbuf;
uint8_t *buf = ops->datbuf;
int ret, subpage;


ops->retlen = 0;
if (!writelen)
return 0;


column = to & (mtd->writesize - 1);
subpage = column || (writelen & (mtd->writesize - 1));


if (subpage && oob)
return -EINVAL;


chipnr = (int)(to >> chip->chip_shift);
chip->select_chip(mtd, chipnr); ? ? ? ? ?//芯片片選,由于各種CPU的片選方式或寄存器不同,或者板子電路不同,所以用戶必須自己實現(xiàn)這個函數(shù)


/* Check, if it is write protected */
if (nand_check_wp(mtd)) {
printk (KERN_NOTICE "nand_do_write_ops: Device is write protected\n");
return -EIO;
}


realpage = (int)(to >> chip->page_shift);
page = realpage & chip->pagemask;
blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;


/* Invalidate the page cache, when we write to the cached page */
if (to <= (chip->pagebuf << chip->page_shift) &&
? ?(chip->pagebuf << chip->page_shift) < (to + ops->len))
chip->pagebuf = -1;


/* If we're not given explicit OOB data, let it be 0xFF */
if (likely(!oob))
memset(chip->oob_poi, 0xff, mtd->oobsize);


/* Don't allow multipage oob writes with offset */
if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))
return -EINVAL;


while (1) { ? ?輸入的長度是塊,只能一頁一頁的寫,所以要循環(huán)寫
WATCHDOG_RESET();


int bytes = mtd->writesize;
int cached = writelen > bytes && page != blockmask;
uint8_t *wbuf = buf;


/* Partial page write ? */
if (unlikely(column || writelen < (mtd->writesize - 1))) {
cached = 0;
bytes = min_t(int, bytes - column, (int) writelen);
chip->pagebuf = -1;
memset(chip->buffers->databuf, 0xff, mtd->writesize);
memcpy(&chip->buffers->databuf[column], buf, bytes);
wbuf = chip->buffers->databuf;
}


if (unlikely(oob)) {
size_t len = min(oobwritelen, oobmaxlen);
oob = nand_fill_oob(chip, oob, len, ops);
oobwritelen -= len;
}


ret = chip->write_page(mtd, chip, wbuf, page, cached, ?//寫一頁,這個函數(shù)有通用的實現(xiàn),若不適合自己的芯片,則需要自己實現(xiàn)頁寫功能
? ? ? (ops->mode == MTD_OOB_RAW));
if (ret)
break;


writelen -= bytes;
if (!writelen)
break;


column = 0;
buf += bytes;
realpage++;


page = realpage & chip->pagemask;
/* Check, if we cross a chip boundary */
if (!page) {
chipnr++;
chip->select_chip(mtd, -1);
chip->select_chip(mtd, chipnr);
}
}


ops->retlen = ops->len - writelen;
if (unlikely(oob))
ops->oobretlen = ops->ooblen;
return ret;
}


再看看通用的頁寫函數(shù)
在函數(shù)int nand_scan_tail(struct mtd_info *mtd)內(nèi)有兩句代碼:
......
if (!chip->write_page)
chip->write_page = nand_write_page;
......


如果用戶沒初始化頁寫函數(shù),則使用默認函數(shù)nand_write_page,這就是需要分析的函數(shù)


static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
? const uint8_t *buf, int page, int cached, int raw)
{
int status;


chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); ? //命令函數(shù)也有默認版本,對單次寫地址的芯片如2440,6410,可以使用默認函數(shù),但是不適合A10
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //A10的地址是兩個寄存器,每個32位,理論可以支持64位的地址寬度
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //這里執(zhí)行nand命令NAND_CMD_SEQIN,值是0x80
if (unlikely(raw)) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//觀察調(diào)用的地方,可以看出 raw = 2 ? ?===> ?ops->mode == MTD_OOB_RAW?
chip->ecc.write_page_raw(mtd, chip, buf); ? ? ? //ecc模塊也有默認實現(xiàn)
else
chip->ecc.write_page(mtd, chip, buf);


/*
* Cached progamming disabled for now, Not sure if its worth the
* trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
*/
cached = 0;


if (!cached || !(chip->options & NAND_CACHEPRG)) {


chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); ? 這里執(zhí)行nand命令NAND_CMD_PAGEPROG,值是0x10
status = chip->waitfunc(mtd, chip); ? ? ? ? ? ? ? //等待寫完成,這個需要用自己實現(xiàn)
* See if operation failed and additional status checks are
* available
*/
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
status = chip->errstat(mtd, chip, FL_WRITING, status,
? ? ? page);


if (status & NAND_STATUS_FAIL)
return -EIO;
} else {
chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
status = chip->waitfunc(mtd, chip);
}


#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
/* Send command to read back the data */
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);


if (chip->verify_buf(mtd, buf, mtd->writesize))
return -EIO;
#endif
return 0;
}


再看看默認的chip->ecc.write_page_raw函數(shù)干了什么事情
在函數(shù)int nand_scan_tail(struct mtd_info *mtd)內(nèi)有兩句代碼:
......
if (!chip->ecc.write_page_raw)
chip->ecc.write_page_raw = nand_write_page_raw;
......
如果用戶沒初始化頁寫函數(shù),則使用默認函數(shù)nand_write_page_raw,這就是需要分析的函數(shù)
這個函數(shù)將數(shù)據(jù)寫入,寫入什么位置呢?還要看看它調(diào)用的函數(shù)chip->write_buf
static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
{
chip->write_buf(mtd, buf, mtd->writesize);?
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
}


再看看默認的chip->write_buf函數(shù)
在函數(shù)int nand_scan_tail(struct mtd_info *mtd)內(nèi)有兩句代碼:
......
if (!chip->write_buf)
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
......
如果用戶沒初始化頁寫函數(shù),8位操作則使用默認函數(shù)nand_write_buf,16位操作則使用默認函數(shù)nand_write_buf16,
cubieboard使用的nand芯片是8位的,就看看nand_write_buf函數(shù)
void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
int i;
struct nand_chip *chip = mtd->priv;


for (i = 0; i < len; i++)
writeb(buf[i], chip->IO_ADDR_W);
}
將數(shù)據(jù)寫入寄存器chip->IO_ADDR_W,即寫到nand緩存
從這里可以看出,上面的寫操作過程就是:
命令0x80-->寫數(shù)據(jù)-->命令0x10-->等待完成
查看cubieboard上面nand芯片K9GBG08U0A的數(shù)據(jù)手冊,頁寫操作的過程真好相同,因此這個驅(qū)動可以使用
使用的前提就是需要實現(xiàn)一下幾個函數(shù):


片選函數(shù): ? ?chip->select_chip
命令操作函數(shù):chip->cmdfunc


chip->waitfunc調(diào)用的兩個函數(shù):
芯片就緒函數(shù):chip->dev_ready
字節(jié)讀取函數(shù):chip->read_byte


到這里,我都沒有分析數(shù)據(jù)結(jié)構(gòu),只描述了調(diào)用流程
觀察各個函數(shù),貫穿整個過程的數(shù)據(jù)結(jié)構(gòu)有兩個
struct mtd_info

struct nand_chip

這兩個數(shù)據(jù)結(jié)構(gòu)在初始化分析時已經(jīng)講過了








轉(zhuǎn)載于:https://www.cnblogs.com/dyllove98/p/3194139.html

與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的u-boot的nand驱动写过程分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。