pywj777 发表于 2006-11-14 11:25:54

移植U-Boot对nand flash支持

对于uboot的移植请参考我之前写的《U-Boot的编译与移植到QT-S3C44B0X开发板上》http://www.linuxfans.org/nuke/modules.php?name=Forums&file=viewtopic&p=4701865&highlight=#4701865

非常感谢dozec的《基于S3C2410的Linux全线移植文档》http://bbs.chinaunix.net/viewthread.php?tid=798304&highlight=基于S3C2410的Linux全线移植文档
我的nand flash移植大部分是参考这个文档移植成功的。

下面对nand flash的初始化代码nand_init()进行分析:
1.如果定义(CONFIG_COMMANDS & CFG_CMD_NAND)没定义(CFG_NAND_LEGACY) 则start_armboot()调用driver/nand/nand.c中的nand_init(),否则如果定义(CONFIG_COMMANDS & CFG_CMD_NAND)并且有定义了CFG_NAND_LEGACY,则调用自己定义的nand_init()。在我当前的情景中是使用driver/nand/nand.c中的nand_init()。
2.nand_init()调用本文件中的nand_init_chip()对nand进行初始化。
3.nand_init_chip()首先调用board_nand_init()。
4.board_nand_init()是需要自己添加的函数,这个函数的主要功能是对struct nand_chip结构体的函数指针赋值,让它们指向自己为nand驱动编写的一些函数,对未赋值的指针,uboot会在后面为其赋上通用nand驱动函数指针。
5.nand_init_chip()接着调用nand_scan().
6.nand_scan()定义在drivers/nand/nand_base.c文件中。它首先对struct nand_chip结构体中在board_nand_init()函数中未赋值的指针赋上通用nand驱动函数指针。
7.通用nand驱动函数nand_select_chip()赋值给struct nand_chip结构体的函数指针用于打开或关闭nand芯片,0为打开,1为关闭。在这个函数中会调用nand_chip结构体中的hwcontrol函数指针,这个指针指向的函数是需要自己编写的。这个函数指针在board_nand_init()函数中被赋值。主要作用是向nand flash发送一些nand flash开启与关闭命令。
8.nand_scan()剩余部分初始化nand_chip和mtd_info结构体。
9.nand_scan()最后在返回时调用drivers/nand/nand_bbt.c文件中的nand_default_bbt()。
10.nand_default_bby()选择一个坏块描述表,返回时调用本文件中的nand_scan_bbt()。
11.nand_scan_bbt()寻找建立一个坏块描述表。
12.最后返回到nand_init(),这样nand驱动的初始化完成了。

下面对命令nand read addr ofs size的执行流程进行分析:
1.nand read addr ofs size命令的作用是从nand flash地址的偏移量ofs处读取长度为size字节的数据存储到内存地址addr处。
2.common/main.c文件中的main_loop()主要执行read_line()读取命令行。
3.read_line()读取到命令行后会调用common/main.c文件中的run_command()。
4.run_command()调用common/command.c文件中的find_cmd()在.u_boot_cmd段中寻找该命令的cmd_tbl_t结构,找到后返回该结构。该命令的结构是通过定义在include/command.h中的宏定义U_BOOT_CMD登记进.u_boot_cmd段中的。
5.run_command()找到该命令的cmd_tbl_t结构后则执行该命令对应的函数。对于本情景是nand命令对应的函数do_nand()。
6.do_nand()有两个版本,一个是定义了CFG_NAND_LEGACY。另一个是未定义CFG_NAND_LEGACY。这两个版本都定义在common/cmd_nand.c文件中。对于本情景使用未定义CFG_NAND_LEGACY的do_nand()函数。要使用do_nand()还必须定义宏CONFIG_COMMANDS&CFG_CMD_NAND。(若未定义CFG_NAND_LEGACY则在这个情景中的do_nand()函数调用的函数都定义在drivers/nand_legacy/nand_legacy.c文件中)。
7.对于我们的情景do_nand()会调用定义在include/nand.h文件中的nand_read()。
8.nand_read()则调用本nand芯片对应的nand_info_t结构的read指针。而read指针在nand_scan()中被指向了同文件(drivers/nand/nand_base.c)中的nand_read()函数。
9.nand_read()函数最终会调用nand_chip结构中的cmdfunc指针,通过这个指针指向的函数向nand flash芯片发送命令。最终完成整个命令的执行。

为了让uboot支持自己QT板子的nand flash而进行修改的部分
1.前面的移植请参考我写的一篇《U-Boot的编译与移植到QT-S3C44B0X开发板上》,现在在board/51EDA/QT/目录下建立nand.c文件。
2.在nand.c中添加自己的board_nand_init()函数。设定nand_chip结构中的hwcontrol和dev_ready指针指向自己的函数QT_hwcontrol和QT_device_ready。并建立自己的QT_hwcontrol和QT_device_ready函数。
3.由于自己板子的nand flash的命令发送方式与uboot提供的通用nand flash命令发送方式不同,所以在nand.c文件中建立自己的命令发送函数QT_nand_command(),并在board_nand_init()函数中将nand_chip结构中的cmdfunc指针指向QT_nand_command()函数,使其使用自己定义的发送命令函数。
4.在include/configs/QT.h中定义CFG_NAND_BASE用于指定自己板子nand flash的I/O地址。
5.在CONFIG_COMMANDS中打开CFG_CMD_NAND选项。
6.在include/configs/QT.h中定义NAND_MAX_CHIPS指定自己板子的nand flash芯片数。
7.在include/configs/QT.h中定义CFG_MAX_NAND_DEVICE指定想要支持的nand flash设备数。

dozec的《基于S3C2410的Linux全线移植文档》在U-BOOT对Nand Flash的支持中的移植方法是在定义了CFG_NAND_LEGACY情况下移植的。而我的是在未定义CFG_NAND_LEGACY的情况下移植的。

pywj777 发表于 2006-11-16 00:54:15

下面是我修改的代码:

include/configs/QT.h
* ----------------------------------------------------------------------------
* NAND Flash configuration
*/
/* NAND Flash I/O port */
#define CFG_NAND_BASE   (0x02000000)
#define QT_NAND_IO      (*(volatile unsigned char *)(0x02000000))
#define QT_CMD_PORT   (*(volatile unsigned char *)(0x02000004))
#define QT_ADDR_PORT    (*(volatile unsigned char *)(0x02000008))


#define QT_GPC_DATA   (0x01D20014)
#define QT_GPE_DATA   (0X01D2002C)
#define QT_READ_REG_ULONG(p)    (*(volatile unsigned long *)(p))
#define QT_WRITE_REG_ULONG(p,v) (*(volatile unsigned long *)(p)) = (v)
/* Chip Enable */
#define MACRO_NAND_ENABLE_CE()\
                (QT_WRITE_REG_ULONG(QT_GPE_DATA, (QT_READ_REG_ULONG(QT_GPE_DATA) & (0xFFFFFFFE))))
/* Chip Disable */
#define MACRO_NAND_DISABLE_CE() \
                (QT_WRITE_REG_ULONG(QT_GPE_DATA, (QT_READ_REG_ULONG(QT_GPE_DATA) | (0x1))))
/* command latch enable */
#define MACRO_NAND_CTL_SETCLE()
/* command latch enable */
#define MACRO_NAND_CTL_CLRCLE()
/* address latch enable */
#define MACRO_NAND_CTL_SETALE()
/* address latch disable */
#define MACRO_NAND_CTL_CLRALE()


/* doc/README.nand need define */
#define NAND_MAX_CHIPS 1
#define CFG_MAX_NAND_DEVICE 1

board/51EDA/QT/nand.c
/*
* (C) Copyright 2006 DENX Software Engineering
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/

#include <common.h>


#if (CONFIG_COMMANDS & CFG_CMD_NAND)

#include <nand.h>

/*
* hardware specific access to control-lines
*/
static void QT_hwcontrol(struct mtd_info *mtdinfo, int cmd)
{
      switch(cmd) {
      case NAND_CTL_SETCLE:
                MACRO_NAND_CTL_SETCLE();
                break;
      case NAND_CTL_CLRCLE:
                MACRO_NAND_CTL_CLRCLE();
                break;
      case NAND_CTL_SETALE:
                MACRO_NAND_CTL_SETALE();
                break;
      case NAND_CTL_CLRALE:
                MACRO_NAND_CTL_CLRALE();
                break;
      case NAND_CTL_SETNCE:
                MACRO_NAND_ENABLE_CE();
                break;
      case NAND_CTL_CLRNCE:
                MACRO_NAND_DISABLE_CE();
                break;
      }
}


/*
* read device ready pin
*/
static int QT_device_ready(struct mtd_info *mtdinfo)
{
      if (QT_READ_REG_ULONG(QT_GPC_DATA) & (0x1<<10)){
                return 1;
      }
      return 0;
}

static void QT_SetCommand( unsigned char command )
{
      volatile int delay;

      for(delay = 8; delay > 0; delay--)
                delay--;

      QT_CMD_PORT = command;

      for(delay = 8; delay > 0; delay--)
                delay--;
}


static void QT_SetAddress( unsigned char address )
{
      volatile int delay;

      for(delay = 8; delay > 0; delay--)
                delay--;

      QT_ADDR_PORT = address;

      for(delay = 8; delay > 0; delay--)
                delay--;

}

/**
* nand_command - [DEFAULT] Send command to NAND device
* @mtd:      MTD device structure
* @command:    the command to be sent
* @column:   the column address for this command, -1 if none
* @page_addr:the page address for this command, -1 if none
*
* Send command to NAND device. This function is used for small page
* devices (256/512 Bytes per page)
*/
static void QT_nand_command (struct mtd_info *mtd, unsigned char command, int column, int page_addr)
{
      register struct nand_chip *this = mtd->priv;

      /* Begin command latch cycle */
      this->hwcontrol(mtd, NAND_CTL_SETCLE);
      /*
         * Write out the command to the device.
         */
      if (command == NAND_CMD_SEQIN) {
                int readcmd;

                if (column >= mtd->oobblock) {
                        /* OOB area */
                        column -= mtd->oobblock;
                        readcmd = NAND_CMD_READOOB;
                } else if (column < 256) {
                        /* First 256 bytes --> READ0 */
                        readcmd = NAND_CMD_READ0;
                } else {
                        column -= 256;
                        readcmd = NAND_CMD_READ1;
                }
                QT_SetCommand(readcmd);
      }

      QT_SetCommand(command);

      /* Set ALE and clear CLE to start address cycle */
      this->hwcontrol(mtd, NAND_CTL_CLRCLE);

      if (column != -1 || page_addr != -1) {
                this->hwcontrol(mtd, NAND_CTL_SETALE);

                /* Serially input address */
                if (column != -1) {
                        /* Adjust columns for 16 bit buswidth */
                        if (this->options & NAND_BUSWIDTH_16)
                              column >>= 1;
                        QT_SetAddress(column);
                }
                if (page_addr != -1) {
                        QT_SetAddress((unsigned char) (page_addr & 0xff));
                        QT_SetAddress((unsigned char) ((page_addr >> 8) & 0xff));
                        /* One more address cycle for devices > 32MiB */
                        if (this->chipsize > (32 << 20))
                              QT_SetAddress((unsigned char) ((page_addr >> 16) & 0x0f));
                }
                /* Latch in address */
                this->hwcontrol(mtd, NAND_CTL_CLRALE);
      }

      /*
         * program and erase have their own busy handlers
         * status and sequential in needs no delay
      */
      switch (command) {

      case NAND_CMD_PAGEPROG:
      case NAND_CMD_ERASE1:
      case NAND_CMD_ERASE2:
      case NAND_CMD_SEQIN:
      case NAND_CMD_STATUS:
                return;

      case NAND_CMD_RESET:
                if (this->dev_ready)
                        break;
                udelay(this->chip_delay);
                this->hwcontrol(mtd, NAND_CTL_SETCLE);
                QT_SetCommand(NAND_CMD_STATUS);
                this->hwcontrol(mtd, NAND_CTL_CLRCLE);
                while ( !(this->read_byte(mtd) & 0x40));
                return;

      /* This applies to read commands */
      default:
                /*
               * If we don't have access to the busy pin, we apply the given
               * command delay
                */
                if (!this->dev_ready) {
                        udelay (this->chip_delay);
                        return;
                }
      }

      /* Apply this short delay always to ensure that we do wait tWB in
         * any case on any machine. */
      ndelay (100);
      /* wait until command is processed */
      while (!this->dev_ready(mtd));
}

/*
* Board-specific NAND initialization. The following members of the
* argument are board-specific (per include/linux/mtd/nand.h):
* - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
* - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
* - hwcontrol: hardwarespecific function for accesing control-lines
* - dev_ready: hardwarespecific function foraccesing device ready/busy line
* - enable_hwecc?: function to enable (reset)hardware ecc generator. Must
*   only be provided if a hardware ECC is available
* - eccmode: mode of ecc, see defines
* - chip_delay: chip dependent delay for transfering data from array to
*   read regs (tR)
* - options: various chip options. They can partly be set to inform
*   nand_scan about special functionality. See the defines for further
*   explanation
* Members with a "?" were not set in the merged testing-NAND branch,
* so they are not set here either.
*/

void board_nand_init(struct nand_chip *nand)
{
      nand->hwcontrol = QT_hwcontrol;
      nand->dev_ready = QT_device_ready;
      nand->eccmode = NAND_ECC_SOFT;
/*      nand->chip_delay = NAND_BIG_DELAY_US;   */
      nand->options = NAND_SAMSUNG_LP_OPTIONS;
      nand->cmdfunc = QT_nand_command;
}
#endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */

大家在移植时可以根据我上面代码分析的线索然后根据自己板子的情况修改上面的代码就几乎可以达到移植的目的了.一般板子的不同之处也就是在于命令的发送和芯片的开启与关闭.还有就是状态的确认.其它代码都是通用的.

lanmanck 发表于 2009-5-21 15:14:06

很好啊,咋没有人顶你?
页: [1]
查看完整版本: 移植U-Boot对nand flash支持