友善之臂Mini2440之嵌入式Linux下应用程序对Nand Flash的读写操作

时间:2022-11-27 18:47:35

应用程序对Nand Flash的读写操作

接上篇继续分析Nand Flash驱动:Nand Flash的设备探测函数s3c24xx_nand_probe的动作。

s3c24xx_nand_probe函数首先创建代表MTD原始设备的数据结构体struct mtd_info *mtd,并将其初始化,源代码如下所示:

/* s3c24xx_nand_probe
 * called by device layer when it finds a device matching one our driver can handled. This code checks to see if
 * it can allocate all necessary resources then calls the nand layer to look for devices
*/
static int s3c24xx_nand_probe(struct platform_device *pdev)
{
 struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
 enum s3c_cpu_type cpu_type;
 struct s3c2410_nand_info *info;
 struct s3c2410_nand_mtd *nmtd;
 struct s3c2410_nand_set *sets;
 struct resource *res;
 int err = 0;
 int size;
 int nr_sets;
 int setno;

 cpu_type = platform_get_device_id(pdev)->driver_data; //获取设备ID

 pr_debug("s3c2410_nand_probe(%p)\n", pdev);

 info = kmalloc(sizeof(*info), GFP_KERNEL);    //创建代表Nand Flash控制器的struct s3c2410_nand_info *info结构体变量
 if (info == NULL) {
  dev_err(&pdev->dev, "no memory for flash info\n");
  err = -ENOMEM;
  goto exit_error;
 }

 memset(info, 0, sizeof(*info));
 platform_set_drvdata(pdev, info);

 spin_lock_init(&info->controller.lock);      //使用自旋锁、初始化等待队列
 init_waitqueue_head(&info->controller.wq);

 /* get the clock source and enable it */ 

//初始化struct s3c2410_nand_info *info结构体变量

 info->clk = clk_get(&pdev->dev, "nand");
 if (IS_ERR(info->clk)) {
  dev_err(&pdev->dev, "failed to get clock\n");
  err = -ENOENT;
  goto exit_error;
 }

 clk_enable(info->clk);

 /* allocate and map the resource */

 /* currently we assume we have the one resource */
 res  = pdev->resource;
 size = res->end - res->start + 1;

 info->area = request_mem_region(res->start, size, pdev->name);

 if (info->area == NULL) {
  dev_err(&pdev->dev, "cannot reserve register region\n");
  err = -ENOENT;
  goto exit_error;
 }

 info->device     = &pdev->dev;
 info->platform   = plat;
 info->regs       = ioremap(res->start, size);
 info->cpu_type   = cpu_type;

 if (info->regs == NULL) {
  dev_err(&pdev->dev, "cannot reserve register region\n");
  err = -EIO;
  goto exit_error;
 }

 dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);

 /* initialise the hardware */

 err = s3c2410_nand_inithw(info);
 if (err != 0)
  goto exit_error;

 sets = (plat != NULL) ? plat->sets : NULL;
 nr_sets = (plat != NULL) ? plat->nr_sets : 1;

 info->mtd_count = nr_sets;

 /* allocate our information */

 size = nr_sets * sizeof(*info->mtds);   //创建Nand Flash硬件设备驱动层的MTD结构体变量struct s3c2410_nand_mtd *mtds
 info->mtds = kmalloc(size, GFP_KERNEL);
 if (info->mtds == NULL) {
  dev_err(&pdev->dev, "failed to allocate mtd storage\n");
  err = -ENOMEM;
  goto exit_error;
 }

 memset(info->mtds, 0, size);

 /* initialise all possible chips */

 nmtd = info->mtds;

 for (setno = 0; setno < nr_sets; setno++, nmtd++) {
  pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);

  s3c2410_nand_init_chip(info, nmtd, sets); //初始化struct nand_chip chip结构体nand芯片底层接口操作函数

  nmtd->scan_res = nand_scan_ident(&nmtd->mtd, //读取Nand Flash设备ID,并与驱动中的Nand Flash ID表比较是否存在
       (sets) ? sets->nr_chips : 1);

  if (nmtd->scan_res == 0) {
   s3c2410_nand_update_chip(info, nmtd);
   nand_scan_tail(&nmtd->mtd);
   s3c2410_nand_add_partition(info, nmtd, sets);  //把创建并初始化好的MTD原始设备添加到MTD原始设备列表中
  }

  if (sets != NULL)
   sets++;
 }

 err = s3c2410_nand_cpufreq_register(info);
 if (err < 0) {
  dev_err(&pdev->dev, "failed to init cpufreq support\n");
  goto exit_error;
 }

 if (allow_clk_stop(info)) {
  dev_info(&pdev->dev, "clock idle support enabled\n");
  clk_disable(info->clk);
 }

 pr_debug("initialised ok\n");
 return 0;

 exit_error:
 s3c24xx_nand_remove(pdev);

 if (err == 0)
  err = -EINVAL;
 return err;
}

二、应用程序对Nand Flash设备的读写

1、s3c2440_nand_hwcontrol函数 直接操作S3C2440 Nand Flash控制器的COMMAND寄存器和ADDRESS寄存器。

static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd,  
       unsigned int ctrl)   //cmd表示要写到nand flash控制器的命令或地址

                                      //ctrl表示要写的是命令还是地址
{
 struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);

 if (cmd == NAND_CMD_NONE)
  return;

 if (ctrl & NAND_CLE)  //命令
  writeb(cmd, info->regs + S3C2440_NFCMD);
 else
  writeb(cmd, info->regs + S3C2440_NFADDR);
}

2、nand_command函数 具体命令要看芯片手册,包括操作序列

/**
 * 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 nand_command(struct mtd_info *mtd, unsigned int command,
    int column, int page_addr)
{
 register struct nand_chip *chip = mtd->priv;
 int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;

 /*
  * Write out the command to the device.
  */
 if (command == NAND_CMD_SEQIN) {
  int readcmd;

  if (column >= mtd->writesize) {
   /* OOB area */
   column -= mtd->writesize;
   readcmd = NAND_CMD_READOOB;
  } else if (column < 256) {
   /* First 256 bytes --> READ0 */
   readcmd = NAND_CMD_READ0;
  } else {
   column -= 256;
   readcmd = NAND_CMD_READ1;
  }
  chip->cmd_ctrl(mtd, readcmd, ctrl);
  ctrl &= ~NAND_CTRL_CHANGE;
 }
 chip->cmd_ctrl(mtd, command, ctrl);

 /*
  * Address cycle, when necessary
  */
 ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
 /* Serially input address */
 if (column != -1) {
  /* Adjust columns for 16 bit buswidth */
  if (chip->options & NAND_BUSWIDTH_16)
   column >>= 1;
  chip->cmd_ctrl(mtd, column, ctrl);
  ctrl &= ~NAND_CTRL_CHANGE;
 }
 if (page_addr != -1) {
  chip->cmd_ctrl(mtd, page_addr, ctrl);
  ctrl &= ~NAND_CTRL_CHANGE;
  chip->cmd_ctrl(mtd, page_addr >> 8, ctrl);
  /* One more address cycle for devices > 32MiB */
  if (chip->chipsize > (32 << 20))
   chip->cmd_ctrl(mtd, page_addr >> 16, ctrl);
 }
 chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);

 /*
  * 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 (chip->dev_ready)
   break;
  udelay(chip->chip_delay);
  chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
          NAND_CTRL_CLE | NAND_CTRL_CHANGE);
  chip->cmd_ctrl(mtd,
          NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
  while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
  return;

  /* This applies to read commands */
 default:
  /*
   * If we don't have access to the busy pin, we apply the given
   * command delay
   */
  if (!chip->dev_ready) {
   udelay(chip->chip_delay);
   return;
  }
 }
 /* Apply this short delay always to ensure that we do wait tWB in
  * any case on any machine. */
 ndelay(100);

 nand_wait_ready(mtd);
}

3、nand_command_lp函数

由于本开发板Mini2440的Nand Flash 256M而且page容量是2Kbytes,用nand_command_lp函数代替nand_command函数

static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
       int column, int page_addr)
{
 register struct nand_chip *chip = mtd->priv;

 /* Emulate NAND_CMD_READOOB */
 if (command == NAND_CMD_READOOB) {
  column += mtd->writesize;
  command = NAND_CMD_READ0;
 }

 /* Command latch cycle */
 chip->cmd_ctrl(mtd, command & 0xff,
         NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);

 if (column != -1 || page_addr != -1) {
  int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;

  /* Serially input address */
  if (column != -1) {
   /* Adjust columns for 16 bit buswidth */
   if (chip->options & NAND_BUSWIDTH_16)
    column >>= 1;
   chip->cmd_ctrl(mtd, column, ctrl);
   ctrl &= ~NAND_CTRL_CHANGE;
   chip->cmd_ctrl(mtd, column >> 8, ctrl);
  }
  if (page_addr != -1) {
   chip->cmd_ctrl(mtd, page_addr, ctrl);
   chip->cmd_ctrl(mtd, page_addr >> 8,
           NAND_NCE | NAND_ALE);
   /* One more address cycle for devices > 128MiB */
   if (chip->chipsize > (128 << 20))
    chip->cmd_ctrl(mtd, page_addr >> 16,
            NAND_NCE | NAND_ALE);
  }
 }
 chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);

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

 case NAND_CMD_CACHEDPROG:
 case NAND_CMD_PAGEPROG:
 case NAND_CMD_ERASE1:
 case NAND_CMD_ERASE2:
 case NAND_CMD_SEQIN:
 case NAND_CMD_RNDIN:
 case NAND_CMD_STATUS:
 case NAND_CMD_DEPLETE1:
  return;

  /*
   * read error status commands require only a short delay
   */
 case NAND_CMD_STATUS_ERROR:
 case NAND_CMD_STATUS_ERROR0:
 case NAND_CMD_STATUS_ERROR1:
 case NAND_CMD_STATUS_ERROR2:
 case NAND_CMD_STATUS_ERROR3:
  udelay(chip->chip_delay);
  return;

 case NAND_CMD_RESET:
  if (chip->dev_ready)
   break;
  udelay(chip->chip_delay);
  chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
          NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
  chip->cmd_ctrl(mtd, NAND_CMD_NONE,
          NAND_NCE | NAND_CTRL_CHANGE);
  while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
  return;

 case NAND_CMD_RNDOUT:
  /* No ready / busy check necessary */
  chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
          NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
  chip->cmd_ctrl(mtd, NAND_CMD_NONE,
          NAND_NCE | NAND_CTRL_CHANGE);
  return;

 case NAND_CMD_READ0:
  chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
          NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
  chip->cmd_ctrl(mtd, NAND_CMD_NONE,
          NAND_NCE | NAND_CTRL_CHANGE);

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

 /* Apply this short delay always to ensure that we do wait tWB in
  * any case on any machine. */
 ndelay(100);

 nand_wait_ready(mtd);
}