Flash设备驱动

在linux系统中,提供了MTD(内存技术设备)系统来建立Flash针对linux的统一,抽象接口,MTD将文件系统与底层的Flash存取器进行了隔离,使得Flash驱动工程师无需关心Flash作为字符设备和块设备与Linux内核接口(由MTD层完成)
在引入MTD后,Linux系统中Flash设备驱动及接口可分四层
(1)硬件驱动层,Flash硬件驱动层负责Flash硬件设备读,写,擦除 LInux MTD 设备的 NOR Flash 芯片驱动位于 drivers/mtd/chips 子目录下, NAND Flash的驱动程序则 位于 drivers/mtd/nand 子目录下。
(2)MTD原始设备层:MTD原始设备层有两部分组成,一部分是MTD原始设备的通用代码,另一部分是特定Flash数据,如分区等
(3)MTD设备层:基于MTD原始设备层,Linux系统可以定义出MTD的块设备(主设备号31)和字符设备(主设备号90)MTD字符设备定义在mtdchar.c中实现,通过注册一系列file_operations的函数(lseek,open,clos,read,write等),可实现设备读写控制,MTD 块设备则是定义在一个描述MTD 块设备的结构 mtdblk_dev ,并声明了一个名为 mtdblks 的指针数组,这个数组 中的每个mtdblk_dev 和 mtd_table 中的每一个mtd_info 一一对应。
(4)块设备节点:通过mknod在/dev子目录下建立MTD字符设备节点和MTD块设备节点
 
LInux MTD系统接口
在引入MTD后,底层Flash驱动直接与MTD原始设备层交互,利用其提供的接口注册设备和分区
用于描述MTD原始设备的数据结构是mtd_info,这其中定义了大量关于MTD的数据和操作函数,mtd_info是表示MTD原始设备结构体,每个分区也被认为是一个mtd_info,这些mtd_info指针被存放在mtd_table的数组里,这个结构体定义如下:
struct mtd_info {
 u_char type;//内存技术类型
 uint32_t flags;//标志位
 uint64_t size;  // MTD设备大小Total size of the MTD
 
 unsigned int erasesize_shift;
 unsigned int writesize_shift;
 
 unsigned int erasesize_mask;
 unsigned int writesize_mask;
 // Kernel-only stuff starts here.
 const char *name;
 int index;
 
 struct nand_ecclayout *ecclayout;
 
 int numeraseregions;
 struct mtd_erase_region_info *eraseregions;
 
 int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
 
 
 int (*point) (struct mtd_info *mtd, loff_t from, size_t len,
   size_t *retlen, void **virt, resource_size_t *phys);
 
 void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
 
 unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
         unsigned long len,
         unsigned long offset,
         unsigned long flags);
 
 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);
 
 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);
 
 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);
 
 int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
 
 void (*sync) (struct mtd_info *mtd);
 
 int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
 int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
 
 int (*suspend) (struct mtd_info *mtd);
 void (*resume) (struct mtd_info *mtd);
 
 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; 
 
 struct mtd_ecc_stats ecc_stats;
 
 int subpage_sft;
 void *priv;
 struct module *owner;
 struct device dev;
 int usecount;
 
 int (*get_device) (struct mtd_info *mtd);
 void (*put_device) (struct mtd_info *mtd);
};
mtd_info的type字段给出了底层物理设备的类型,包括MTD_RAM,MTD_ROM,MTD_NORFLASH,MTD_NANSFLASH等
flags字段标志可以是MTD_WRITEABLE,MTD_BIT_WRITEABLE,MTD_NO_ERASE,MTD_POWERUP_LOCK等的组合
mtd_info中的read(),write(),read_oob(),write_oob(),erase()是MTD设备驱动要实现的主要函数,在Linux中MTD下层实现了针对NOR Flash和NAND Flash通用的mtd_info成员函数
某些内存技术带有额外数据(OOB),例如NAND Flash没512字节就会有16个字节的“额外数据”
 
Flash驱动中使用如下两个函数注册和注销MTD设备
int add_mtd_device(struct mtd_info *mtd);
int del_mtd_device(struct mtd_info *mtd);
下面代码中mtd_part结构体用于表示分区,其mtd_info成员结构体用于描述分区,它会被加入到mtd_table(定义为struct mtd_info *mtd_table[MAX_MTD_DEVICES])中,其大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区相应的函数
struct mtd_part {
 struct mtd_info mtd;//分区信息(大部分由master决定)
 struct mtd_info *master;//该分区的主分区
 uint64_t offset;//该分区的偏移地址
 int index;// 主分区号
 struct list_head list;
 int registered;
};
mtd_partition(分区数组)会在MTD原始设备层调用add_mtd_partitions()时传递分区信息用,定义如下:


struct mtd_partition {
 char *name;   //标示字符串
 uint64_t size;   //分区大小
 uint64_t offset;  //主MTD内偏移地址
 uint32_t mask_flags;  //掩码标志
 struct nand_ecclayout *ecclayout; //OOB布局out of band layout for this partition (NAND only)
 struct mtd_info **mtdp;  
};
Flash驱动中使用如下两个函数注册和注销分区
int add_mtd_partitions(struct mtd_info *master,struct mtd_partition *parts,int nbparts)
int del_mtd_partitions(struct mtd_info *master);
add_mtd_partitions()会对每一个新建立的分区建立一个mtd_part结构体,并将其加入mtd_partions(分区数组)中,并调用add_mtd_device()将此分区作为MTD设备键入mtd_table,成功时返回0,否则返回-ENOMEM
del_mtd_partitions()的作用是对于mtd_partitions上的每一个分区,如果它的主分区是master(要被删除的分区号),则将它从mtd_partitions和mtd_table中删除,这是要调用del_mtd_device;
add_mtd_partitions()中新建的mtd_part需要依赖于传进的mtd_partition(分区数组)参数对其进行初始化
int add_mtd_partitions(struct mtd_info *master,
         const struct mtd_partition *parts,
         int nbparts)
{
 struct mtd_part *slave;
 uint64_t cur_offset = 0;
 int i;
 printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
 for (i = 0; i < nbparts; i++) {
  slave = add_one_partition(master, parts + i, i, cur_offset);
  if (!slave)
   return -ENOMEM;
  cur_offset = slave->offset + slave->mtd.size;
 }
 return 0;
}
static struct mtd_part *add_one_partition(struct mtd_info *master,
  const struct mtd_partition *part, int partno,
  uint64_t cur_offset)
{
 struct mtd_part *slave;
 
 slave = kzalloc(sizeof(*slave), GFP_KERNEL);
 if (!slave) {
  printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
   master->name);
  del_mtd_partitions(master);
  return NULL;
 }
 list_add(&slave->list, &mtd_partitions);
 
 slave->mtd.type = master->type;
 slave->mtd.flags = master->flags & ~part->mask_flags;
 slave->mtd.size = part->size;
 slave->mtd.writesize = master->writesize;
 slave->mtd.oobsize = master->oobsize;
 slave->mtd.oobavail = master->oobavail;
 slave->mtd.subpage_sft = master->subpage_sft;
 slave->mtd.name = part->name;
 slave->mtd.owner = master->owner;
 slave->mtd.backing_dev_info = master->backing_dev_info;
...
}
 
MTD用户空间编程
drives/mtd/mtdchar.c文件实现了MTD字符设备接口,通过它用户可以直接操作Flash设备,通过read(),write系统调用可以读写Flash,通过一系列IOCTL命令可以获得Flash设备信息,擦除Flash,读写NAND的OOB等
 
NOR Flash驱动
在Linux中,实现了针对CFI(公共Flash接口),JEDEC等接口的通用NOR驱动,这一层驱动直接面对mtd_info的成员函数,这使得NOR的芯片级驱动变得十分简单,只需定义具体内存映射情况结构体mao_info并使用指定调用do_mao_probe()探测mtd_info()就可以了
NOR Flash驱动的核心是定义map_info结构体,他指定NOR Flash的基址,位宽,大小等信息,然后调用do_map_probe()探测芯片就可以了,map_info原型如下:
struct map_info {
 const char *name;
 unsigned long size;
 resource_size_t phys;
#define NO_XIP (-1UL)
 void __iomem *virt;
 void *cached;
 int bankwidth;
#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
 map_word (*read)(struct map_info *, unsigned long);
 void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);
 void (*write)(struct map_info *, const map_word, unsigned long);
 void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
 
#endif
 
 void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
 
 void (*set_vpp)(struct map_info *, int);
 unsigned long pfow_base;
 unsigned long map_priv_1;
 unsigned long map_priv_2;
 void *fldrv_priv;
 struct mtd_chip_driver *fldrv;
};
NOR Flash驱动在Linux中实现,主要工作如下:
(1)定义map_info的实例,初始化其中的成员,根据目标板的情况name,size,bankwidth和phys赋值
(2)如果Flash需要分区,则定义mtd_partition数组,将实际电路板中Flash分区信息记录在其中
(3)以map_info和探测的接口类型(如“ch_probe”,"jedec_probe"等)为参数调用do_map_probe()探测Flash得到mtd_info
do_map_probe()函数原型如下:
struct mtd_info *do_map_probe(const char *name,struct map_info *map);
第一个为探测接口类型,常见的如下
do_map_probe("cfi_probe",&xxx_map_info);
do_map_probe("jedec_probe",&xxx_map_info);
do_map_probe("map_rom",&xxx_map_info);
do_map_probe()会根据传入参数name(接口类型),通过get_mtd_chip_driver()得到具体的MTD驱动,如下所示:
struct mtd_info *do_map_probe(const char *name, struct map_info *map)
{
 struct mtd_chip_driver *drv;
 struct mtd_info *ret;
 drv = get_mtd_chip_driver(name);
 if (!drv && !request_module("%s", name))
  drv = get_mtd_chip_driver(name);//通过接口类型找到驱动
 if (!drv)
  return NULL;
 ret = drv->probe(map);
 
 module_put(drv->module);
 if (ret)
  return ret;
 return NULL;

(4)在模块初始化时以mtd_info为参数调用add_mtd_device()或以mtd_info,partition数组为参数调用add_mtd_partitions()注册设备或分区
(5)在模块卸载时调用“反函数”来删除设备或分区
下面是一个NOR Flash的驱动
#define WINDOW_SIZE ...
#define WINDOW_ADDR ...
static struct map_info xxx_map = {//定义并初始化map_info
.name = "xxx_Flash",
.size = WINDOW_SIZE,//大小
.bankwidth = 1,//总线宽度
.phys = WINDOW_ADDR//物理地址
};
 
static struct mtd_partition xxx_partitions[] = {//定义数组用于分区
{
.name = "Drive A",
.offset = 0,//分区偏移地址
.size = 0x0e0000//分区大小
},
...
};
 
#define NUM_PARTITIONS ARRAY_SIZE(xxx_partitions)
 
static struct mtd_info *mymtd;
 
static int __init init_xxx_map(void)
{
int rc = 0;
 
xxx_map.virt = ioremap_nocache(xxx_map.phys,xxx_map.size);//物理地址映射为虚拟地址
...
mymtd = do_map_probe("jedec_probe",&xxx_map);//探测NOR Flash得到mtd_info
mymtd->owner = THIS_MODULE;
add_mtd_partitions(mymtd,xxx_partitions,NUM_PARTTITIONS);//添加分区信息
...
 
}
struct void __exit cleanup_xxx_map(void0
{
if(mymtd){
del_mtd_partitions(mymtd);//删除分区
map_destroy(mymtd);
}
...

 
NAND Flash驱动
和NOR Flash非常类似,在Linux内核MTD的下层已经实现了通用的NAND驱动(在driver/mtd/nand/nand_bash.c中)即实现了mtd_info结构体的成员函数
MTD使用nand_chip数据结构表示一个NAND Flash芯片,这个结构体中包含了关于NAND Flash的地址信息,读写方法,ECC模式等,这个结构体原型如下:
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;
};
与NOR Flash一样,由于有了MTD层,完成一个NAND Flash驱动在Linux中的工作量也很少,主要工作如下:
(1)如果Flash需要分区,则定义mtd_partition数组,将实际电路板中Flash分区信息记录以其中
(2)在模块加载是分配nand_chip的内存,根据目标板NAND控制器的情况初始化nand_chip中的cmd_ctrl(),dev_ready(),ready_byte(),write_buf()等成员(如果不赋值就使用nand_base.c中的默认函数),注意将mtd_info的priv指向nand_chip
(3)以mtd_info为参数调用nand_scan()函数探测NAND Flash的存在得到mtd_info,nand_scan()函数原型如下:
int nand_scan(struct mtd_info *mtd,int maxchips);
(4)如果要分区则以mtd_info,mtd_partition为参数调用add_mtd_partitions()函数,添加分区
下面是一个NAND Flash设备驱动模块
#define CHIP_PHYSICAL_ADDRESS  ...
#define NUM_PARTITIONS 2
static const struct mtd_partition partition_info[] = {
 {
  .name = "NAND FS 0",
  .offset = 0,
  .size = 8 * 1024 * 1024},
 {
  .name = "NAND FS 1",
  .offset = MTDPART_OFS_APPEND,//表示接着上面结束地址
  .size = MTDPART_SIZ_FULL}
};
static int __init au1xxx_nand_init(void)
{
 struct nand_chip *this;
 u16 boot_swapboot = 0; 
 int retval;
 u32 mem_staddr;
 u32 nand_phys;
 
//初始化结构体(分配内存)
 au1550_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
 if (!au1550_mtd) {
  printk("Unable to allocate NAND MTD dev structure.\n");
  return -ENOMEM;
 }
 
 this = (struct nand_chip *)(&au1550_mtd[1]);
 
//初始化成员函数
 memset(au1550_mtd, 0, sizeof(struct mtd_info));
 memset(this, 0, sizeof(struct nand_chip));
 
 au1550_mtd->priv = this;
 au1550_mtd->owner = THIS_MODULE;


 
 au_writel(0, MEM_STNDCTL);
#ifdef CONFIG_MIPS_PB1550
 
 au_writel(au_readl(GPIO2_DIR) & ~(1 << 6), GPIO2_DIR);
 boot_swapboot = (au_readl(MEM_STSTAT) & (0x7 << 1)) | ((bcsr->status >> 6) & 0x1);
 switch (boot_swapboot) {
 case 0:
 case 2:
 case 8:
 case 0xC:
 case 0xD:
  
  nand_width = 0;
  break;
 case 1:
 case 9:
 case 3:
 case 0xE:
 case 0xF:
  
  nand_width = 1;
  break;
 default:
  printk("Pb1550 NAND: bad boot:swap\n");
  retval = -EINVAL;
  goto outmem;
 }
#endif
 
#ifdef NAND_STCFG
 if (NAND_CS == 0) {
  au_writel(NAND_STCFG,  MEM_STCFG0);
  au_writel(NAND_STTIME, MEM_STTIME0);
  au_writel(NAND_STADDR, MEM_STADDR0);
 }
 if (NAND_CS == 1) {
  au_writel(NAND_STCFG,  MEM_STCFG1);
  au_writel(NAND_STTIME, MEM_STTIME1);
  au_writel(NAND_STADDR, MEM_STADDR1);
 }
 if (NAND_CS == 2) {
  au_writel(NAND_STCFG,  MEM_STCFG2);
  au_writel(NAND_STTIME, MEM_STTIME2);
  au_writel(NAND_STADDR, MEM_STADDR2);
 }
 if (NAND_CS == 3) {
  au_writel(NAND_STCFG,  MEM_STCFG3);
  au_writel(NAND_STTIME, MEM_STTIME3);
  au_writel(NAND_STADDR, MEM_STADDR3);
 }
#endif
 
 mem_staddr = 0x00000000;
 if (((au_readl(MEM_STCFG0) & 0x7) == 0x5) && (NAND_CS == 0))
  mem_staddr = au_readl(MEM_STADDR0);
 else if (((au_readl(MEM_STCFG1) & 0x7) == 0x5) && (NAND_CS == 1))
  mem_staddr = au_readl(MEM_STADDR1);
 else if (((au_readl(MEM_STCFG2) & 0x7) == 0x5) && (NAND_CS == 2))
  mem_staddr = au_readl(MEM_STADDR2);
 else if (((au_readl(MEM_STCFG3) & 0x7) == 0x5) && (NAND_CS == 3))
  mem_staddr = au_readl(MEM_STADDR3);
 if (mem_staddr == 0x00000000) {
  printk("Au1xxx NAND: ERROR WITH NAND CHIP-SELECT\n");
  kfree(au1550_mtd);
  return 1;
 }
 nand_phys = (mem_staddr << 4) & 0xFFFC0000;
 p_nand = (void __iomem *)ioremap(nand_phys, 0x1000);
 
 if (NAND_CS == 0)
  nand_width = au_readl(MEM_STCFG0) & (1 << 22);
 if (NAND_CS == 1)
  nand_width = au_readl(MEM_STCFG1) & (1 << 22);
 if (NAND_CS == 2)
  nand_width = au_readl(MEM_STCFG2) & (1 << 22);
 if (NAND_CS == 3)
  nand_width = au_readl(MEM_STCFG3) & (1 << 22);
 
 this->dev_ready = au1550_device_ready;
 this->select_chip = au1550_select_chip;
 this->cmdfunc = au1550_command;
 
 this->chip_delay = 30;
 this->ecc.mode = NAND_ECC_SOFT;
 this->options = NAND_NO_AUTOINCR;
 if (!nand_width)
  this->options |= NAND_BUSWIDTH_16;
 this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte;
 au1550_write_byte = (!nand_width) ? au_write_byte16 : au_write_byte;
 this->read_word = au_read_word;
 this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf;
 this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf;
 this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf;
 
 if (nand_scan(au1550_mtd, 1)) {//探测NAND Flash
  retval = -ENXIO;
  goto outio;
 }
 
//注册分区
 add_mtd_partitions(au1550_mtd, partition_info, ARRAY_SIZE(partition_info));
 return 0;
 outio:
 iounmap((void *)p_nand);
 outmem:
 kfree(au1550_mtd);
 return retval;
}
最后强调的是,在NAND芯片驱动中,如果nand_chip中没有赋值,将使用默认值(nand_base.c中的默认值),下面是nand_chip中的nand_ecc_ctrl结构体类型成员ecc赋值,定义OOB的分布模式
static struct nand_ecclayout nand_oob_8 = {
 .eccbytes = 3,
 .eccpos = {0, 1, 2},
 .oobfree = {
  {.offset = 3,
   .length = 2},
  {.offset = 6,
   .length = 2}}
};
static struct nand_ecclayout nand_oob_16 = {
 .eccbytes = 6,
 .eccpos = {0, 1, 2, 3, 6, 7},
 .oobfree = {
  {.offset = 8,
   . length = 8}}
};
static struct nand_ecclayout nand_oob_64 = {
 .eccbytes = 24,
 .eccpos = {
     40, 41, 42, 43, 44, 45, 46, 47,
     48, 49, 50, 51, 52, 53, 54, 55,
     56, 57, 58, 59, 60, 61, 62, 63},
 .oobfree = {
  {.offset = 2,
   .length = 38}}
};
static struct nand_ecclayout nand_oob_128 = {
 .eccbytes = 48,
 .eccpos = {
     80, 81, 82, 83, 84, 85, 86, 87,
     88, 89, 90, 91, 92, 93, 94, 95,
     96, 97, 98, 99, 100, 101, 102, 103,
     104, 105, 106, 107, 108, 109, 110, 111,
     112, 113, 114, 115, 116, 117, 118, 119,
     120, 121, 122, 123, 124, 125, 126, 127},
 .oobfree = {
  {.offset = 2,
   .length = 78}}
};
NOR Flash驱动实例
针对S3C2410等平台而言。外接NOR Flash的情况下,由于NOR Flash直接映射到CPU的内存空间,为了使用NOR Flash驱动,我们只需要在BSP的板文件中添加相应的信息, 例如NOR Flash所在的物理地址和到校,分区信息,总线宽度等,这些信息以platform资源和数据的形式呈现,如下:
static struct resource nor_resource = {
.start = OMAP_CS0_PHYS,
.end = OMAP_CS0_PHYS + SZ_32M - 1,
.flags = IORESOURCE_MEM,//表示地址
};
static struct mtd_partition nor_partitions[] = {//分区结构体
{
     .name = "bootloader",
     .offset = 0,
     .size = SZ_128K,
     .mask_flags = MTD_WRITEABLE,
},
{
     .name = "params",
     .offset = MTDPART_OFS_APPEND,
     .size = SZ_128K,
     .mask_flags = 0,
},
{
     .name = "kernel",
     .offset = MTDPART_OFS_APPEND,
     .size = SZ_2M,
     .mask_flags = 0
},
{
     .name = "rootfs",
     .offset = MTDPART_OFS_APPEND,
     .size = MTDPART_SIZ_FULL,
     .mask_flags = 0
},
};


static struct flash_platform_data nor_data = {
.map_name = "cfi_probe",
.width = 2,
.parts = nor_partitions,
.nr_parts = ARRAY_SIZE(nor_partitions),
};
static struct platform_device nor_device = {
.name = "omapflash",//应该和驱动里的(physmap.c)一致
.id = 0,
.dev = {
.platform_data = &nor_data,
},
.num_resources = 1,//资源数
.resource = &nor_resource,//资源结构体
};
下面来总结一下:
由于引入了MTD系统以及MTD下层的通用NOR和NAND驱动,Linux中NOR和NAND Flash芯片级驱动的设计难度大大降低,对于NOR驱动工作仅仅只需在BSP中添加相关的platform信息。
在串口驱动中,讲解了tty_driver到uart_driver的角色转换,在Flash驱动中,讲解了mtd_info向map_info/nand_chip的转移,可以说,Linux驱动这种分层设计思想是贯穿个中Linux驱动框架的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值