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這個函數可不是打雜的.

 

 

 

發佈了14 篇原創文章 · 獲贊 8 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章