编译系统 :ubuntu 16.04
硬件平台 :jz2240
交叉编译器:arm-linux-gcc 4.3.2
再移植完nor flash 之后,发现u-boot还不支持nand flash,串口打印为 :
nand_get_flash_type: second ID read did not match 0e,ec against 0b,0b
No NAND device found!!!
根据 "second ID read did not match"定位到代码 driver/mtd/nand/nand_base.c中nand_get_flash_type函数(2645行),如下:
static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip,
int busw,
int *maf_id, int *dev_id,
const struct nand_flash_dev *type)
{
int i, maf_idx;
u8 id_data[8];
int ret;
/* Select the device */
chip->select_chip(mtd, 0);
/*
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
* after power-up
*/
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
*maf_id = chip->read_byte(mtd);
*dev_id = chip->read_byte(mtd);
/* Try again to make sure, as some systems the bus-hold or other
* interface concerns can cause random data which looks like a
* possibly credible NAND flash to appear. If the two results do
* not match, ignore the device completely.
*/
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
for (i = 0; i < 2; i++)
id_data[i] = chip->read_byte(mtd);
if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
printk(KERN_INFO "%s: second ID read did not match "
"%02x,%02x against %02x,%02x\n", __func__,
*maf_id, *dev_id, id_data[0], id_data[1]);
return ERR_PTR(-ENODEV);
}
、、、、、//余下代码省略
}
该部分代码主要意思是,对nand flash进行两次读ID,这样做是因为但有时候系统总线或者其他接口的干扰会产生随机值从而造成nand flash 可用的假象,把两次读取的ID号进行比较,当且仅当两次都一样才认为nand flash是可用的。根据串口信息提示,显然两次读取的ID不一样。
/* 读flash类型 */
nand_get_flash_type(mtd, chip, busw,&nand_maf_id, &nand_dev_id, table);
{
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
*maf_id = chip->read_byte(mtd);
*dev_id = chip->read_byte(mtd);
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
for (i = 0; i < 2; i++)
id_data[i] = chip->read_byte(mtd);
if (!type) //type是NULL
type = nand_flash_ids;//type指向该数组的首地址
for (; type->name != NULL; type++)//开始在数组中根据ID号进行遍历
if (*dev_id == type->id)//根据K9F2G08U0C(NAND FLASH)手册36页,看到maker code = 0xec,device code = Oxda,因此可以查到
break;
其中:
const struct nand_flash_dev nand_flash_ids[] = {
//省略其余无关代码
/*
* These are the new chips with large page size. The pagesize and the
* erasesize is determined from the extended id bytes
*/
#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR)
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
/* 2 Gigabit */
{"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS},
{"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS},
{"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16},
{"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16},
//省略其余无关代码
{NULL,}
}
发现该段代码并没有什么错误,于是看谁调用了nand_get_flash,调用关系如下
arch/arm/lib/board.c
540行:puts("NAND: ");
nand_init(); /* go init the NAND */
driver/mtd/nand/nand.c
105行:void nand_init(void)
{
#ifdef CONFIG_SYS_NAND_SELF_INIT //此宏没有定义,故下面的不执行
board_nand_init();
#else //执行以下代码
int i;
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) //CONFIG_SYS_MAX_NAND_DEVICE = 1
nand_init_chip(i); //重点看它的实现
#endif
printf("%lu MiB\n", total_nand_size / 1024);
#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
/* Select the chip in the board/cpu specific driver*/
board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
#endif
}
===================================================================================
//初始化nand
static void nand_init_chip(int i)
{
struct mtd_info *mtd = &nand_info[i];
struct nand_chip *nand = &nand_chip[i];
ulong base_addr = base_address[i];
//base_addr = 0x4E000000
int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;
if (maxchips < 1)
maxchips = 1;
mtd->priv = nand;
nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;//这个赋值不起作用,在后面会被覆盖掉
/* 这里可能出现问题 */
if (board_nand_init(nand))
return;
/* 这里可能出现问题 */
if (nand_scan(mtd, maxchips))
return;
nand_register(i);
}
=========================================================================================
driver/mtd/nand/s3c2410_nand.c
183行:int board_nand_init(struct nand_chip *nand)
{
u_int32_t cfg;
u_int8_t tacls, twrph0, twrph1;
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();
debug("board_nand_init()\n");
writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);
/* initialize hardware */
#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING) //宏没有定义
tacls = CONFIG_S3C24XX_TACLS;
twrph0 = CONFIG_S3C24XX_TWRPH0;
twrph1 = CONFIG_S3C24XX_TWRPH1;
#else //下面的代码生效
tacls = 4;
twrph0 = 8;
twrph1 = 8;
#endif
cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1); //cfg |= (tacls - 1) << 8
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); //cfg |= (twrph0 - 1) << 4
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); //cfg |= (twrph1 - 1) << 0
writel(cfg, &nand_reg->nfconf); //根据s3c2440手册,这里寄存器位设置错误
/* 把上面的注释并修改如下 */
cfg = ((tacls )<<12)|((twrph0 )<<8)|((twrph1)<<4);
//cfg = ((tacls )<<12)|((twrph0 )<<8)|((twrph1)<<4)|(3<<2)|(1<<1)|(0<<0);
writel(cfg, &nand_reg->nfconf);
/* 使能Nand flash控制器,初始化ECC,禁止片选 */
/* 上面代码没有,需要添加*/
cfg = (1<<4)|(1<<1)|(1<<0);
writel(cfg, &nand_reg->nfcont);
/* initialize nand_chip data structure */
nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
nand->IO_ADDR_W = (void *)&nand_reg->nfdata;
nand->select_chip = NULL;
/* read_buf and write_buf are default */
/* read_byte and write_byte are default */
#ifdef CONFIG_NAND_SPL
nand->read_buf = nand_read_buf;
#endif
/* hwcontrol always must be implemented */
nand->cmd_ctrl = s3c2410_hwcontrol; //这里可能有问题,进去看一下
nand->dev_ready = s3c2410_dev_ready; //这里面的s3c2410都修改为s3c2440
#ifdef CONFIG_S3C2410_NAND_HWECC //没有定义宏
nand->ecc.hwctl = s3c2410_nand_enable_hwecc;
nand->ecc.calculate = s3c2410_nand_calculate_ecc;
nand->ecc.correct = s3c2410_nand_correct_data;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
#else
nand->ecc.mode = NAND_ECC_SOFT;
#endif
#ifdef CONFIG_S3C2410_NAND_BBT
nand->options = NAND_USE_FLASH_BBT;
#else
nand->options = 0;
#endif
debug("end of nand_init\n");
return 0;
}
===================================================================================
/* 硬件控制,主要是片选,发读写数据,命令,地址 */
static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
*/
struct s3c2440_nand *nand = s3c2440_get_base_nand();//return (struct s3c2440_nand *)0x4E000000
debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
#if 0
//此段代码完全可以代替下面的代码,但是为了减少在原代码修改量,不用
if (ctrl & NAND_CLE)
{
/* 发命令 */
writel(cmd, &nand->nfcmd);
}
else if (ctrl & NAND_ALE)
{
/* 发命令 */
writel(cmd, &nand->nfaddr);
}
//当使用此代码时,还需要自己写一个 片选使能 函数,在board_nand_init中找到下面代码
/* 这里是没有片选函数,但是在后面函数会用到 */
nand->select_chip = NULL;//s3c2440_select_chip;
修改为:
nand->select_chip = s3c2440_select_chip;
static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
switch (chipnr)
{
case -1: /* 取消选中 */
nand->nfcont |= (1 << 1);
break;
case 0:
nand->nfcont &= ~(1 << 1);
break;
default:
BUG();
}
}
#endif
#if 1
/* 放在这里为了方便看
struct s3c2440_nand
{ struct s3c2410_nand
u32 nfconf; {
u32 nfcont; u32 nfconf;
u32 nfcmd; u32 nfcmd;
u32 nfaddr; u32 nfaddr;
u32 nfdata; u32 nfdata;
u32 nfeccd0; u32 nfstat;
u32 nfeccd1; u32 nfecc;
u32 nfeccd; };
u32 nfstat;
u32 nfstat0;
u32 nfstat1;
};*/
/* 1.这里不确定是否有问题 */
if (ctrl & NAND_CTRL_CHANGE) //nand ctrl change
{
ulong IO_ADDR_W = (ulong)nand;
if (!(ctrl & NAND_CLE))
IO_ADDR_W |= 0xc;
//IO_ADDR_W |= S3C2410_ADDR_NCLE; //S3C2410_ADDR_NCLE = 8根据s3c2440_nand 的定义,非命令,话句话说就是地址,于是IO_ADDR_W = 0x4E000008,这是s3c2410的寄存器,s3c2440不是这样,IO_ADDR_W |= 0xc, 即IO_ADDR_W = 0x4E00000c
if (!(ctrl & NAND_ALE)) 同理如下
IO_ADDR_W |= 8;
//IO_ADDR_W |= S3C2410_ADDR_NALE;
chip->IO_ADDR_W = (void *)IO_ADDR_W;
if ((!(ctrl & NAND_ALE)) && (!(ctrl & NAND_CLE)))
chip->IO_ADDR_W = (void *)IO_ADDR_W+4;
/* 2.这里被修改了,原本都是nfconf修改后nfcont*/
/* 3.根据修改,这里应该是没有问题的 */
if (ctrl & NAND_NCE)
writel(readl(&nand->nfcont) & ~S3C2440_NFCONF_nFCE,&nand->nfcont);
else
writel(readl(&nand->nfcont) | S3C2440_NFCONF_nFCE,&nand->nfcont);
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, chip->IO_ADDR_W);
#endif
}
至此,u-boot启动之后就能检测到nand flash并支持nand flash操作!注意不是从nand flash启动!