arm-linux东东之nand之2:3c2440_nand_probe

 3c2440_nand_probe

如果你没有意见.我们开始进入3c2440_nand_probe.这人函数可是干活的家伙.故事就是从这里开始的.

static int s3c2440_nand_probe(struct platform_device *dev)

{

       return s3c24xx_nand_probe(dev, TYPE_S3C2440);

}

static int s3c24xx_nand_probe(struct platform_device *pdev,

                           enum s3c_cpu_type cpu_type)

{

       struct s3c2410_platform_nand *plat = to_nand_plat(pdev);

       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;

 

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

 

       info = kmalloc(sizeof(*info), GFP_KERNEL);

/*

   

*/

       if (info == NULL) {

       dev_err(&pdev->dev, "no memory for flash info/n");

       err = -ENOMEM;

       goto exit_error;

       }

 

       memzero(info, 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 */

 

       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, pdev);

       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);

       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;

       }

 

       memzero(info->mtds, 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);

 

       nmtd->scan_res = nand_scan_ident(&nmtd->mtd,

                                           (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);

       }

 

       if (sets != NULL)

       sets++;

       }

 

       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:

       s3c2410_nand_remove(pdev);

 

       if (err == 0)

              err = -EINVAL;

       return err;

}    

好家伙.那么长.吓人一大跳.还好很多函数都是明白的. Plat就当初我们说的

static struct s3c2410_platform_nand smdk_nand_info = {

       .tacls              = 20,

       .twrph0          = 60,

       .twrph1          = 20,

       .nr_sets   = ARRAY_SIZE(smdk_nand_sets),

       .sets        = smdk_nand_sets,

};

info = kmalloc(sizeof(*info), GFP_KERNEL);info 分配内存.

platform_set_drvdata(pdev, info);pdevinfo关联.

这些都是比较统一函数结构.

来看下这个函数: s3c2410_nand_inithw

static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,

                            struct platform_device *pdev)

{

       struct s3c2410_platform_nand *plat = to_nand_plat(pdev);

       unsigned long clkrate = clk_get_rate(info->clk);

       int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;

       int tacls, twrph0, twrph1;

       unsigned long cfg = 0;

 

       /* calculate the timing information for the controller */

 

       clkrate /= 1000;      /* turn clock into kHz for ease of use */

if (plat != NULL) {

              tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max);

              twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8);

              twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8);

       } else {

              /* default timings */

              tacls = tacls_max;

              twrph0 = 8;

              twrph1 = 8;

       }

 

       if (tacls < 0 || twrph0 < 0 || twrph1 < 0) {

              dev_err(info->device, "cannot get suitable timings/n");

              return -EINVAL;

       }

 

       dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns/n",

              tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));

 

      switch (info->cpu_type) {

      case TYPE_S3C2410:

              cfg = S3C2410_NFCONF_EN;

              cfg |= S3C2410_NFCONF_TACLS(tacls - 1);

              cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);

              cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);

              break;

 

      case TYPE_S3C2440:

      case TYPE_S3C2412:

              cfg = S3C2440_NFCONF_TACLS(tacls - 1);

              cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);

              cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);

 

              /* enable the controller and de-assert nFCE */

 

              writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);

       }

 

       dev_dbg(info->device, "NF_CONF is 0x%lx/n", cfg);

 

       writel(cfg, info->regs + S3C2410_NFCONF);

       return 0;

}

这个函数是总线的设定.如何设定呢?先来说下几个数据.

 

 

 

 

 
 

Tacls是当CLE/ALE有效时过了多少时间后nwe才有效.

TWRPH0nwe的有效时间.

TWRPH1是当nWE无效后DATA的保持时间.

 

这里tcs,twp,tclh就是我们要的

       .tacls              = 20,

       .twrph0          = 60,

       .twrph1          = 20,

注意单位是ns.

好回到我们的s3c2410_nand_inithw中来.

tacls_max是怎么算的呢?还不是S3C2440 TACLS只占了二位.二位就是4. =====真叨

clkrate NAND的源时钟./1000把它转换成KHZ

plat是不为NULL,于是

#define NS_IN_KHZ 1000000

 

static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)

{

       int result;

 

       result = (wanted * clk) / NS_IN_KHZ;

       result++;

 

       pr_debug("result %d from %ld, %d/n", result, clk, wanted);

 

       if (result > max) {

              printk("%d ns is too big for current clock rate %ld/n", wanted, clk);

              return -1;

       }

 

       if (result < 1)

              result = 1;

 

       return result;

}

这里怎么算呢.举个例子:(1/HZ)*n=20ns

于是n=HZ*(20ns)由于这里的单位是ns 所于除了NS_IN_KHZ纯数学问题very simple.

Result 算出来以后就返回了.

好返回s3c2410_nand_inithw中来:

twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8);

twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8);

这两个也是一样的.

来看一下switch(info->cpu_type)这个句子

 

       case TYPE_S3C2440:

      case TYPE_S3C2412:

              cfg = S3C2440_NFCONF_TACLS(tacls - 1);

              cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);

              cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);

这里为什么减一呢.原来是这样的

 

 
 
 
 

算的时候它自动加1.三星的东西很多都是这样的.这里算的时候也不是很严的时间限制.有没有注意到上面result++.不管怎么样误差不是太大就行了.

writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);

#define S3C2440_NFCONT_ENABLE         (1<<0)

NFCONF有个开始/禁止控制位.这里开启使nand控制器跑起来.

s3c2410_nand_inithw完了.返回到s3c24xx_nand_probe中来.

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

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

set就是开始那个.

set _sets 就是1,表示我只有一块NAND.

 

805行分配一个mtd. Info是这么样的一个结构体.

struct s3c2410_nand_info {

       /* mtd info */

       struct nand_hw_control        controller;

       struct s3c2410_nand_mtd            *mtds;

       struct s3c2410_platform_nand      *platform;

 

       /* device info */

       struct device                 *device;

       struct resource                     *area;

       struct clk               *clk;

       void __iomem               *regs;

       void __iomem               *sel_reg;

       int                         sel_bit;

       int                         mtd_count;

       unsigned long                save_sel;

 

       enum s3c_cpu_type              cpu_type;

};

struct s3c2410_nand_mtd {

       struct mtd_info                    mtd;

       struct nand_chip           chip;

       struct s3c2410_nand_set              *set;

       struct s3c2410_nand_info     *info;

       int                         scan_res;

};

 

其中mtds里面的mtd就表示NAND.它表示所有分区的master.如果没有分区的话.这个mtd就会添加到分区表中去.如果有分区.则不会添加到分区中.而是作为所有分区的master.

816 nmtd指向我们刚才分配的mtds

 

 
 
 
 

到了818.那个for只会循环一次.因为我们的nr_sets1.

进入s3c2410_nand_init_chip 一段一段来:

 

 

 
 
 

602行使chip指向nmtd内的chip.chip就表示一块芯片.这是高一层的结构体.

/**

 * struct nand_chip - NAND Private Flash Chip Data

 * @IO_ADDR_R:        [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device

 * @IO_ADDR_W:              [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device

 * @read_byte:             [REPLACEABLE] read one byte from the chip

 * @read_word:           [REPLACEABLE] read one word from the chip

 * @write_buf:             [REPLACEABLE] write data from the buffer to the chip

 * @read_buf:              [REPLACEABLE] read data from the chip into the buffer

 * @verify_buf:            [REPLACEABLE] verify buffer contents against the chip data

 * @select_chip:    [REPLACEABLE] select chip nr

 * @block_bad:            [REPLACEABLE] check, if the block is bad

 * @block_markbad:     [REPLACEABLE] mark the block bad

 * @cmd_ctrl:              [BOARDSPECIFIC] hardwarespecific funtion for controlling

 *                  ALE/CLE/nCE. Also used to write command and address

 * @dev_ready:            [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line

 *                  If set to NULL no access to ready/busy is available and the ready/busy information

 *                  is read from the chip status register

 * @cmdfunc:              [REPLACEABLE] hardwarespecific function for writing commands to the chip

 * @waitfunc:              [REPLACEABLE] hardwarespecific function for wait on ready

 * @ecc:        [BOARDSPECIFIC] ecc control ctructure

 * @buffers:         buffer structure for read/write

 * @hwcontrol:            platform-specific hardware control structure

 * @ops:        oob operation operands

 * @erase_cmd:           [INTERN] erase command write function, selectable due to AND support

 * @scan_bbt:              [REPLACEABLE] function to scan bad block table

 * @chip_delay:            [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)

 * @wq:               [INTERN] wait queue to sleep on if a NAND operation is in progress

 * @state:             [INTERN] the current state of the NAND device

 * @oob_poi:        poison value buffer

 * @page_shift:            [INTERN] number of address bits in a page (column address bits)

 * @phys_erase_shift:   [INTERN] number of address bits in a physical eraseblock

 * @bbt_erase_shift:     [INTERN] number of address bits in a bbt entry

 * @chip_shift:             [INTERN] number of address bits in one chip

 * @datbuf:           [INTERN] internal buffer for one page + oob

 * @oobbuf:          [INTERN] oob buffer for one eraseblock

 * @oobdirty:        [INTERN] indicates that oob_buf must be reinitialized

 * @data_poi:        [INTERN] pointer to a data buffer

 * @options:         [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about

 *                  special functionality. See the defines for further explanation

 * @badblockpos:  [INTERN] position of the bad block marker in the oob area

 * @cellinfo:         [INTERN] MLC/multichip data from chip ident

 * @numchips:             [INTERN] number of physical chips

 * @chipsize:        [INTERN] the size of one chip for multichip arrays

 * @pagemask:             [INTERN] page number mask = number of (pages / chip) - 1

 * @pagebuf:        [INTERN] holds the pagenumber which is currently in data_buf

 * @subpagesize:   [INTERN] holds the subpagesize

 * @ecclayout:             [REPLACEABLE] the default ecc placement scheme

 * @bbt:        [INTERN] bad block table pointer

 * @bbt_td:           [REPLACEABLE] bad block table descriptor for flash lookup

 * @bbt_md:         [REPLACEABLE] bad block table mirror descriptor

 * @badblock_pattern:  [REPLACEABLE] bad block scan pattern used for initial bad block scan

 * @controller:             [REPLACEABLE] a pointer to a hardware controller structure

 *                  which is shared among multiple independend devices

 * @priv:              [OPTIONAL] pointer to private chip date

 * @errstat:           [OPTIONAL] hardware specific function to perform additional error status checks

 *                  (determine if errors are correctable)

 * @write_page:           [REPLACEABLE] High-level page write function

 */

 

struct nand_chip {

          //数据写地址

       void  __iomem      *IO_ADDR_R;

       //数据读地址

       void  __iomem      *IO_ADDR_W;

 

       uint8_t    (*read_byte)(struct mtd_info *mtd);

       ////////////////////////

       u16         (*read_word)(struct mtd_info *mtd);

       ///////////////////////

       void        (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);

       void        (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);

       int           (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);

       void        (*select_chip)(struct mtd_info *mtd, int chip);

       int           (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);

       int           (*block_markbad)(struct mtd_info *mtd, loff_t ofs);

       ///////////////////////////////////////////////////

       void        (*cmd_ctrl)(struct mtd_info *mtd, int dat,

                                unsigned int ctrl);

       //命令-----数据地址命令

       int           (*dev_ready)(struct mtd_info *mtd);

       void        (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);

       int           (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);

       void        (*erase_cmd)(struct mtd_info *mtd, int page);

       int           (*scan_bbt)(struct mtd_info *mtd);

       int           (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);

       int           (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,

                                  const uint8_t *buf, int page, int cached, int raw);

 

       int           chip_delay;

       unsigned int    options;

 

       int           page_shift;

       int           phys_erase_shift;

       int           bbt_erase_shift;

       int           chip_shift;

       int           numchips;

       unsigned long  chipsize;

       int           pagemask;

       int           pagebuf;

       int           subpagesize;

       uint8_t           cellinfo;

       int           badblockpos;

 

       nand_state_t   state;

 

       uint8_t           *oob_poi;

       struct nand_hw_control  *controller;

       struct nand_ecclayout    *ecclayout;

 

       struct nand_ecc_ctrl ecc;

       struct nand_buffers *buffers;

       struct nand_hw_control hwcontrol;

 

       struct mtd_oob_ops ops;

 

       uint8_t           *bbt;

       struct nand_bbt_descr   *bbt_td;

       struct nand_bbt_descr   *bbt_md;

 

       struct nand_bbt_descr   *badblock_pattern;

 

       void        *priv;

};

如果不是在大学混了三年.看到这样的结构休.我老早就溜了.

还好俺也算知识混子

605612chip初始化.这些值用到的时候我们再来说.我会时时提醒你.

624IO_ADDR_W 写地址NFDATA. 三星规定了写数据就住这里写.

info->sel_reg   = regs + S3C2410_NFCONF;

info->sel_bit   = S3C2410_NFCONF_nFCE;

chip->cmd_ctrl  = s3c2410_nand_hwcontrol;

chip->dev_ready = s3c2410_nand_devready;

NFCONT有这么样一个位:

 

 

 

0表示Enable chip select

s3c2410_nand_select_chip中我们会用上的.待得瞧.

回来看s3c2410_nand_init_chip:

chip->IO_ADDR_R = chip->IO_ADDR_W;

 

nmtd->info        = info;

nmtd->mtd.priv         = chip;

nmtd->mtd.owner    = THIS_MODULE;

nmtd->set         = set;

读地址与写地址是一样.

传说中的ECC出来了.

static int hardware_ecc = 1;表示要ECC.什么是ECC:就是对数据的保护.数据传送有没有出错.ECC有两种.一种是通过软件计算出的.一种是通过硬件算出来的.S3C2440来说它的ECC是硬件算出来的.怎么算????当写数据时:例如写512个数据到NAND,512写完以后ECC就会被算出,放在NFMCCD0-NFMCCD2.这个过程是全自动的.

 

 

但有人就会问生成以后呢.??有没有注意到上面.512来说其实 1 page=528

多了16.ECC就是放在这16个当中的.

NAND_ECC_SOFT表示软件生成ECC.

683set->disable_ecc如果为1就表示是hi ECC你不用检测了..

我们顺便把s3c2410_nand_calculate_ecc也看了.

static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)

{

       struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);

 

       ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0);

       ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);

       ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);

 

       pr_debug("%s: returning ecc %02x%02x%02x/n", __func__,

               ecc_code[0], ecc_code[1], ecc_code[2]);

 

       return 0;

}

这个函数就是读一下ECC寄存器.

s3c2410_nand_init_chip就这样完了.chip还没有完.回到s3c24xx_nand_probe中来

823行调用nand_scan_ident这个函数可不是打杂的.

 

 

 

你可能感兴趣的:(c,function,struct,null,buffer,Descriptor)