友善之臂Mini2440之嵌入式Linux上Nand Flash相关操作流程

2019-07-13 08:44发布

       嵌入式Linux系统采用MTD子系统来进行存储的访问和管理。MTD(Memory Technology Device)是用于访问memory设备的Linux子系统。MTD的主要目的是为了使新的memory设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。MTD设备可分为四层,这四层从上到下分别是设备节点、MTD设备层、MTD原始设备层和硬件驱动层。 (1)、Flash硬件驱动层:负责驱动Flash硬件 (2)、MTD原始设备层:一部分是MTD该层的通用代码;另一部分是各个特定的Flash的数据。 重要的数据结构:struct mtd_info,其中定义了大量的关于MTD的数据和操作函数。 struct mtd_info {
u_char type;
uint32_t flags;
uint64_t size;// Total size of the MTD


/* "Major" erase size for the device. Na茂ve users may take this
* to be the only erase size available, or may use the more detailed
* information below if they desire
*/
uint32_t erasesize;
/* Minimal writable flash unit size. In case of NOR flash it is 1 (even
* though individual bits can be cleared), in case of NAND flash it is
* one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
* it is of ECC block size, etc. It is illegal to have writesize = 0.
* Any driver registering a struct mtd_info must ensure a writesize of
* 1 or larger.
*/
uint32_t writesize;


uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
uint32_t oobavail;  // Available OOB bytes per block


/*
* If erasesize is a power of 2 then the shift is stored in
* erasesize_shift otherwise erasesize_shift is zero. Ditto writesize.
*/
unsigned int erasesize_shift;
unsigned int writesize_shift;
/* Masks based on erasesize_shift and writesize_shift */
unsigned int erasesize_mask;
unsigned int writesize_mask;


// Kernel-only stuff starts here.
const char *name;
int index;


/* ecc layout structure pointer - read only ! */
struct nand_ecclayout *ecclayout;


/* Data for variable erase regions. If numeraseregions is zero,
* it means that the whole device has erasesize as given above.
*/
int numeraseregions;
struct mtd_erase_region_info *eraseregions;


/*
* Erase is an asynchronous operation.  Device drivers are supposed
* to call instr->callback() whenever the operation completes, even
* if it completes with a failure.
* Callers are supposed to pass a callback function and wait for it
* to be called before writing to the block.
*/
int (*erase) (struct mtd_info *mtd, struct erase_info *instr);


/* This stuff for eXecute-In-Place */
/* phys is optional and may be set to NULL */
int (*point) (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys);


/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);


/* Allow NOMMU mmap() to directly map the device (if not NULL)
* - return the address to which the offset maps
* - return -ENOSYS to indicate refusal to do the mapping
*/
unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
   unsigned long len,
   unsigned long offset,
   unsigned long flags);


/* Backing device capabilities for this device
* - provides mmap capabilities
*/
struct backing_dev_info *backing_dev_info;




int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);


/* In blackbox flight recorder like scenarios we want to make successful
  writes in interrupt context. panic_write() is only intended to be
  called when its known the kernel is about to panic and we need the
  write to succeed. Since the kernel is not going to be running for much
  longer, this function can break locks and delay to ensure the write
  succeeds (but not sleep). */


int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);


int (*read_oob) (struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
int (*write_oob) (struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops);


/*
* Methods to access the protection register area, present in some
* flash devices. The user data is one time programmable but the
* factory data is read only.
*/
int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);


/* kvec-based read/write methods.
  NB: The 'count' parameter is the number of _vectors_, each of
  which contains an (ofs, len) tuple.
*/
int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);


/* Sync */
void (*sync) (struct mtd_info *mtd);


/* Chip-supported device locking */
int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);


/* Power Management functions */
int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd);


/* Bad block management functions */
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);


struct notifier_block reboot_notifier;  /* default mode before reboot */


/* ECC status information */
struct mtd_ecc_stats ecc_stats;
/* Subpage shift (NAND) */
int subpage_sft;


void *priv;


struct module *owner;
struct device dev;
int usecount;


/* If the driver is something smart, like UBI, it may need to maintain
* its own reference counting. The below functions are only for driver.
* The driver may register its callbacks. These callbacks are not
* supposed to be called by MTD users */
int (*get_device) (struct mtd_info *mtd);
void (*put_device) (struct mtd_info *mtd);
};
而特定的Flash数据则放在/drivers/mtd/maps/子目录下,每个文件都描述了一块板子上的Flash。 3)、MTD设备层:Linux系统可以定义出MTD的块设备(主设备号31,mtdblock.c文件,mtdblk_dev结构)和字符设备(设备号90,mtdchar.c)。 static struct mtdblk_dev{
struct mtd_info *mtd;
int count;
struct mutex cache_mutex;
unsigned char *cache_data;
unsigned long cache_offset;
unsigned int cache_size;
enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
} *mtdblks[MAX_MTD_DEVICES];
(4)、设备节点:通过mknod在/dev子目录下建立MTD字符设备节点和MTD块设备节点,通过访问此设备节点极客访问MTD字符设备和块设备。
二、NandFlash驱动中的重要结构体 1、struct s3c2410_nand_info 表示一个nand Flash控制器相关信息。 /**
 * struct s3c2410_nand_info - NAND controller state.
 * @mtds: An array of MTD instances on this controoler.
 * @platform: The platform data for this board.
 * @device: The platform device we bound to.
 * @area: The IO area resource that came from request_mem_region().
 * @clk: The clock resource for this controller.
 * @regs: The area mapped for the hardware registers described by @area.
 * @sel_reg: Pointer to the register controlling the NAND selection.
 * @sel_bit: The bit in @sel_reg to select the NAND chip.
 * @mtd_count: The number of MTDs created from this controller.
 * @save_sel: The contents of @sel_reg to be saved over suspend.
 * @clk_rate: The clock rate from @clk.
 * @cpu_type: The exact type of this controller.
 */
struct s3c2410_nand_info {
/* mtd info */
struct nand_hw_controlcontroller;
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;
unsigned long  clk_rate;


enum s3c_cpu_typecpu_type;


#ifdef CONFIG_CPU_FREQ
struct notifier_blockfreq_transition;
#endif
}
2、struct s3c2410_nand_mtd表示Nand Flash硬件设备驱动层的MTD设备结构体。 * struct s3c2410_nand_mtd - driver MTD structure
 * @mtd: The MTD instance to pass to the MTD layer.
 * @chip: The NAND chip information.
 * @set: The platform information supplied for this set of NAND chips.
 * @info: Link back to the hardware information.
 * @scan_res: The result from calling nand_scan_ident().
*/
struct s3c2410_nand_mtd {
struct mtd_infomtd;
struct nand_chipchip;
struct s3c2410_nand_set*set;
struct s3c2410_nand_info*info;
int scan_res;
};
3、struct nand_chip定义了具体的物理Nand Flash芯片最底层的硬件接口操作函数及硬件相关信息。   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)
 * @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
 * @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;
uint64_t  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;
};
4、struct s3c2410_nand_set定义了nand Flash芯片的分区信息和名字。 * struct s3c2410_nand_set - define a set of one or more nand chips
 * @disable_ecc: Entirely disable ECC - Dangerous
 * @flash_bbt: Openmoko u-boot can create a Bad Block Table
 * Setting this flag will allow the kernel to
 * look for it at boot time and also skip the NAND
 * scan.
 * @nr_chips: Number of chips in this set
 * @nr_partitions: Number of partitions pointed to by @partitions
 * @name: Name of set (optional)
 * @nr_map: Map for low-layer logical to physical chip numbers (option)
 * @partitions: The mtd partition list
 *
 * define a set of one or more nand chips registered with an unique mtd. Also
 * allows to pass flag to the underlying NAND layer. 'disable_ecc' will trigger
 * a warning at boot time.
 */
struct s3c2410_nand_set {
unsigned int  disable_ecc:1;
unsigned int  flash_bbt:1;


int nr_chips;
int nr_partitions;
char *name;
int *nr_map;
struct mtd_partition*partitions;
struct nand_ecclayout*ecc_layout;
};