移植uboot第六步:支持NANDFlash

写在前面:

我的博客已迁移至自建服务器:博客传送门,CSDN博客暂时停止,如有机器学习方面的兴趣,欢迎来看一看。

此外目前我在gitHub上准备一些李航的《统计学习方法》的实现算法,目标将书内算法全部手打实现,欢迎参观并打星。GitHib传送门

正文

前面已经让板子能够支持NORFlash了,还没有支持NANDFlash。

一. 找到之前第一步注释掉的NAND相关的宏定义

//#define CONFIG_S3C2410		/* specifically a SAMSUNG S3C2410 SoC */
#ifdef CONFIG_S3C2410
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#define CONFIG_SYS_MAX_NAND_DEVICE	1
#define CONFIG_SYS_NAND_BASE		0x4E000000
#endif

为了程序的兼容性,不直接把宏定义取消注释。在宏定义边上再宏定义一个

//#define CONFIG_S3C2410		/* specifically a SAMSUNG S3C2410 SoC */
#define CONFIG_S3C2440		/* specifically a SAMSUNG S3C2410 SoC */

然后将NAND的相关宏定义格式改为

#ifdef CONFIG_S3C2410
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#else
#define CONFIG_NAND_S3C2440
#define CONFIG_SYS_S3C2440_NAND_HWECC
#endif
#define CONFIG_SYS_MAX_NAND_DEVICE	1
#define CONFIG_SYS_NAND_BASE		0x4E000000
#endif

这样这个uboot以后对2410还是有很好的支持性的。

编译,解决错误

二. 报错:s3c2410_nand.c的第72行出现错误。(一般出现一堆错误的时候,解决第一个错误,解决完再重新编译, 再解决第一个错误,直到没有错误。因为很多错误是有连带性的,一个错误解决可能会连带解决一些错误)
找到代码段,是调用了一个结构体,结构体的定义是这样的
struct s3c2410_nand *nand = s3c2410_get_base_nand();
定位到这个结构体的初始化

#ifdef CONFIG_S3C2410
/* NAND FLASH (see S3C2410 manual chapter 6) */
struct s3c2410_nand {
u32nfconf;
u32nfcmd;
u32nfaddr;
u32nfdata;
u32nfstat;
u32nfecc;
};

#endif
#ifdef CONFIG_S3C2440
/* NAND FLASH (see S3C2440 manual chapter 6) */
struct s3c2440_nand {
	u32	nfconf;
	u32	nfcont;
	u32	nfcmd;
	u32	nfaddr;
	u32	nfdata;
	u32	nfeccd0;
	u32	nfeccd1;
	u32	nfeccd;
	u32	nfstat;
	u32	nfstat0;
	u32	nfstat1;
};
#endif

这里可以明白,我们使用的宏是2440,所以2410这个结构体并没有,在代码中把2410改为2440。
我们干脆就重新做一个s3c2440_nand.c文件,找到目录drivers\mtd\nand里面的s3c2410_nand.c,复制一份改为s3c2440_nand.c,要将其加入uboot,所以找到该文件目录里的Makefile,找到里面有一条语句

COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o

说明原先的2410也是条件编译进入uboot。在include\configs 目录内找到smdk2440.h。

#ifdef CONFIG_CMD_NAND

#ifdef CONFIG_S3C2410
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#else
#define CONFIG_NAND_S3C2440
#define CONFIG_SYS_S3C2440_NAND_HWECC
#endif

#define CONFIG_SYS_MAX_NAND_DEVICE	1
#define CONFIG_SYS_NAND_BASE		0x4E000000
#endif

宏定义在这里,这段代码段就是我们刚才修改过的,也就是说s3c2410_nand.c本身就已经不参与编译了,我们再边上加上一句

COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o

如果定义了CONFIG_NAND_S3C2440,那么就编译s3c2440_nand.c
这样就可以了。

进入s3c2440_nand.c目录。将刚才发现错误的地方修改如下

struct s3c2440_nand *nand = s3c2440_get_base_nand();

三.查看nand相关函数是否支持2440
board_init_r中找到

nand_init();		/* go init the NAND */

进入函数

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++)
		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
}

查看得知CONFIG_SYS_NAND_SELF_INIT这个宏定义是否定义由另一个宏定义决定

#if defined(CONFIG_NAND_FSL_ELBC)
#define CONFIG_SYS_NAND_SELF_INIT
#endif

CONFIG_NAND_FSL_ELBC没有被定义,也就是说程序是执行的是#else下面的内容,进入nand_init_chip(i);函数。

#ifndef CONFIG_SYS_NAND_SELF_INIT
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];
	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);
}
#endif

进入board_nand_init看看

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 s3c2440_nand *nand_reg = s3c2440_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 |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
	cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
	writel(cfg, &nand_reg->nfconf);

	/* initialize nand_chip data structure */
	nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
	nand->IO_ADDR_W = (void *)&nand_reg->nfdata;

	nand->select_chip = s3c2440_nand_select;

	/* 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 = s3c2440_hwcontrol;

	nand->dev_ready = s3c2440_dev_ready;

#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;
}

下面这段代码应该是设置NAND控制器的时间参数的。我当时写的时候三个时间参数设置是

tacls    = 0;
twrph0 = 1;
twrph1 = 0;

但是无所谓,只要它的时间参数比我的久就行了,久是肯定可以直接用的,比我的时间短那就得看手册支不支持怎么短的时间了。

tacls    = 4;
twrph0 = 8;
twrph1 = 8;
#endif

cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
writel(cfg, &nand_reg->nfconf);

查看手册比对寄存器设置的时候这些代码是否是对的,我就不想管这些了,直接粗暴一点,反正之前这块设置的代码还在,我就直接改成了

#else
	tacls    = 4;
	twrph0 = 8;
	twrph1 = 8;
#endif

#if 0
	cfg = S3C2410_NFCONF_EN;
	cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
	cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
	cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
#else
	/* 设置时序 */        
	cfg = ((twrph1 - 1)<<12)|((twrph0 - 1)<<8)|((tacls - 1)<<4);        
#endif
	writel(cfg, &nand_reg->nfconf);

设置完时序以后 初始化ECC, 禁止片选还没有做,我也自己给他在后面加上

/* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */        
writel( (1<<4)|(1<<1)|(1<<0), &nand_reg->nfcont);

函数剩下的部分是结构体参数的赋值,应该是NAND的底层操作函数在这里上报。

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 = s3c2440_hwcontrol;

	nand->dev_ready = s3c2440_dev_ready;

#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

我们要看现在uboot的nand能不能支持2440了,就一个个把这些函数看下去,看每一个函数里面的代码是否和2440匹配。

a.select_chip函数设置的是null,就先不管,CONFIG_NAND_SPL没有宏定义,所以read_buf也先不管,先看nand->cmd_ctrl = s3c2410_hwcontrol;这个函数
先把名字都从2410改成2440。

static void s3c2440_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
	struct nand_chip *chip = mtd->priv;
	struct s3c2440_nand *nand = s3c2440_get_base_nand();

	debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);

	if (ctrl & NAND_CTRL_CHANGE) {
		ulong IO_ADDR_W = (ulong)nand;

		if (!(ctrl & NAND_CLE))
			IO_ADDR_W |= S3C2410_ADDR_NCLE;
		if (!(ctrl & NAND_ALE))
			IO_ADDR_W |= S3C2410_ADDR_NALE;

		chip->IO_ADDR_W = (void *)IO_ADDR_W;

		if (ctrl & NAND_NCE)
			writel(readl(&nand->nfconf) & ~S3C2410_NFCONF_nFCE,
			       &nand->nfconf);
		else
			writel(readl(&nand->nfconf) | S3C2410_NFCONF_nFCE,
			       &nand->nfconf);
	}

	if (cmd != NAND_CMD_NONE)
		writeb(cmd, chip->IO_ADDR_W);
}

这个函数应该是负责写命令或者写数据,直接将其修改为我们自己的

static void s3c2440_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
	struct s3c2440_nand *nand = s3c2440_get_base_nand();

	if (ctrl & NAND_CLE)
	{
		/* 发命令 */
		writeb(dat, &nand->nfcmd);
	}
	else if (ctrl & NAND_ALE)
	{
		/* 发数据 */
		writeb(dat, &nand->nfaddr);
	}
}

接下来就是比对每一个操作函数里面的操作寄存器的方式和我们的是否一样,同时把所有的2440修改为2410。

之前代码里看到过select_chip 函数是空,那么怎么选中片子呢?

nand->select_chip = null;

一层层函数下去找到nand_set_defaults函数,看名字是设置默认值

void board_init_r(gd_t *id, ulong dest_addr)
nand_init();		/* go init the NAND */
nand_init_chip(i);
nand_scan(mtd, maxchips)
nand_scan_ident(mtd, maxchips, NULL);
nand_set_defaults(chip, busw);

这个函数的作用是,查看之前上报用的结构体,如果某项为空,就使用程序自带的默认函数。

/*
 * Set default functions
 */
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
	/* check for proper chip_delay setup, set 20us if not */
	if (!chip->chip_delay)
		chip->chip_delay = 20;

	/* check, if a user supplied command function given */
	if (chip->cmdfunc == NULL)
		chip->cmdfunc = nand_command;

	/* check, if a user supplied wait function given */
	if (chip->waitfunc == NULL)
		chip->waitfunc = nand_wait;

	if (!chip->select_chip)
		chip->select_chip = nand_select_chip;
	if (!chip->read_byte)
		chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
	if (!chip->read_word)
		chip->read_word = nand_read_word;
	if (!chip->block_bad)
		chip->block_bad = nand_block_bad;
	if (!chip->block_markbad)
		chip->block_markbad = nand_default_block_markbad;
	if (!chip->write_buf)
		chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
	if (!chip->read_buf)
		chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
	if (!chip->verify_buf)
		chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
	if (!chip->scan_bbt)
		chip->scan_bbt = nand_default_bbt;
	if (!chip->controller)
		chip->controller = &chip->hwcontrol;
}

找到它默认的选择片子的函数

/**
 * nand_select_chip - [DEFAULT] control CE line
 * @mtd:	MTD device structure
 * @chipnr:	chipnumber to select, -1 for deselect
 *
 * Default select function for 1 chip devices.
 */
static void nand_select_chip(struct mtd_info *mtd, int chipnr)
{
	struct nand_chip *chip = mtd->priv;

	switch (chipnr) {
	case -1:
		chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
		break;
	case 0:
		break;

	default:
		BUG();
	}
}

-1表示取消选中,0表示选中,这里只写了取消选中,没写选中,显然这个函数我们是用不了的。自己在s3c2440_nand.c建一个函数

static void s3c2440_nand_select(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();
	}
}

在结构体上报中修改一下

nand->select_chip = s3c2440_nand_select;

编译,烧写,运行。
没有错误且已经能识别NAND了,因为烧写在NAND里,0地址对应的是片内内存,无法去访问NOR,所以NOR识别出来应该是0k。但是NOR启动的话,两个都能识别。

U-Boot 2012.04.01 (Aug 12 2016 - 16:02:01)

CPUID: 32440001
FCLK:      400 MHz
HCLK:      100 MHz
PCLK:       50 MHz
DRAM:  64 MiB
WARNING: Caches not enabled
Flash: 0 KB
NAND:  256 MiB
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   CS8900-0
SMDK2410 # 

后记:
boot有四五百K,我使用的是oflash,烧写速度很慢,提速的方式可以使用先烧写下的好用的uboot,我使用了韦东山的,两百多K,然后再使用好用的uboot去驱动dnw下载。
现在我们写的boot已经能使用串口了,因此可以使用串口下载,速度也还可以。
输入help查看指令,有loady下载方式

loady   - load binary file over serial line (ymodem mode)

我使用的Xshell没找到loady模式,(load有好几种模式,loadx,loady,loadz,大多数都不支持loady)。百度下载了一个XP的超级终端,提供loady。终端中串口连接上,输入命令。

loady 0x30000000

后面的是烧写地址。板子就在等待文件,终端中选择boot路径,选择loady模式,点确定就能下载。发送文件这个步骤手速要快,如果失败了,就是时间间隔太久了,可以复制文件路径,进去直接粘贴点确定,要快一点。
烧写进去以后是在板子的内存里,输入

nand erase 0 80000		//擦除nand, 从0地址一直到80000
nand write 0 80000		//将内存中的boot写入nand,从0地址到80000
reset					//重启boot
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值