nand flash驱动
编写Nand Flash驱动
由于MTD设备驱动已经帮我实现了MTD块设备、以及MTD字符设备驱动的编写。而我们要做的主要就是:
-
- 分配nand_chip内存;
-
- 根据SOC Nand控制器初始化nand_chip成员,比如:chip->legacy(成员write_buf、read_buf、select_chip、cmd_ctrl、dev_ready、IO_ADDR_R、IO_ADDR_W)、chip->controller;
-
- 设置chip->priv为mtd_info;
-
以mtd_info为参数调用nand_scan探测Nand Flash,nand_scan会读取nand芯片ID:
- 初始化chip->base.mtd(成员writesize、oobsize、erasesize等);
- 初始化chip->base.memorg(成员bits_per_cell、pagesize、oobsize、pages_per_eraseblock、planes_per_lun、luns_per_target、ntatgets等);
- 初始化chip->options、chip->base.eccreq;
- chip成员中所有未初始化函数指针则使用nand_base.c中的默认函数;
- 初始化chip->ecc各个成员(设置ecc模式及处理函数);
-
- mtd_info和mtd_partition为参数调用mtd_device_register()进行MTD设备注册;
1 项目结构
我们在/work/sambashare/drivers路径下创建项目17.nand_flash_dev,创建为nand_flash_dev.c。
2 模块入口函数
-
-
使用kzalloc函数为nand_chip动态申请内存,其中nand_chip.base.mtd包含了mtd_info结构体成员;
-
使用ioremap将Nand Flash控制器寄存器地址映射到虚地址空间;
-
设置mtd_info结构体成员;
-
设置nand_chip结构体成员;
-
获取nand时钟,设置info->clk;并使能nand时钟、Nanfd Flash控制器;
-
以mtd_info为参数调用nand_scan探测Nand Flash;
-
mtd_info和mtd_partition为参数调用mtd_device_register()进行MTD设备注册;
-
3 模块出口函数
-
- 卸载MTD设备;
- 取消寄存器地址映射;
- 释放nand_chip;
nand_flash_dev.c
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
/*
* Nand Flash控制器相关寄存器
*/
struct nand_regs {
unsigned long nfconf ; //0x4E000000
unsigned long nfcont ;
unsigned long nfcmd ;
unsigned long nfaddr ;
unsigned long nfdata ;
unsigned long nfeccd0 ;
unsigned long nfeccd1 ;
unsigned long nfeccd ;
unsigned long nfstat ;
unsigned long nfestat0;
unsigned long nfestat1;
unsigned long nfmecc0 ;
unsigned long nfmecc1 ;
unsigned long nfsecc ;
unsigned long nfsblk ;
unsigned long nfeblk ;
};
/* 全局变量 */
static struct nand_regs *s3c_regs;
static struct mtd_info *s3c_mtd;
static struct nand_chip *s3c_nand_chip;
/* 分区信息 */
static struct mtd_partition s3c_nand_part[] = {
[0] = {
.name = "u-boot",
.size = SZ_256K,
.offset = 0,
},
[1] = {
.name = "params",
.size = SZ_128K,
.offset = MTDPART_OFS_APPEND,
},
[2] = {
.name = "kernel",
/* 5 megabytes, for a kernel with no modules
* or a uImage with a ramdisk attached */
.size = SZ_4M,
.offset = MTDPART_OFS_APPEND,
},
[3] = {
.name = "rootfs",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
},
};
/*
* 使能/禁止片选
*/
static void s3c_nand_select_chip(struct nand_chip *this, int chip)
{
if(chip==-1) //CE Disable
{
s3c_regs->nfcont|=(0x01<<1); //bit1置1
}
else //CE Enable
{
s3c_regs->nfcont&=~(0x01<<1); //NFCONT寄存器片选位设置位0,使能片选
}
}
/*
* 用于向nand芯片发送命令/地址,第三个参数用来区分是发送的是命令还是地址。
*/
static void s3c_nand_cmd_ctrl(struct nand_chip *chip, int cmd, unsigned int ctrl)
{
if (ctrl & NAND_CLE) //当前为command状态 ,
s3c_regs->nfcmd=cmd;
else //当前为地址状态
s3c_regs->nfaddr=cmd;
}
/*
* 用于获取nand的状态,0表示繁忙,1表示就绪:
*/
static int s3c_nand_devready(struct mtd_info *mtd)
{
return (s3c_regs->nfstat&0x01); //获取RnB状态,0:busy 1:ready
}
/*
* 用于从nand读取len个长度字节,并保存到buf缓冲区中:
*/
static void s3c_nand_read_buf(struct nand_chip *this, u_char *buf, int len)
{
readsl(&s3c_regs->nfdata, buf, len >> 2); // 读取NFDATA寄存器的值,读取长度位len >> 2,按字访问
/* cleanup if we've got less than a word to do */
if (len & 3) { // 处理长度非4整数倍情况
buf += len & ~3;
for (; len & 3; len--)
*buf++ = readb(&s3c_regs->nfdata); // 按字节读取
}
}
/*
* 用于将缓冲区buf中len个长度字节写入到nand
*/
static void s3c_nand_write_buf(struct nand_chip *this, const u_char *buf,int len)
{
writesl(&s3c_regs->nfdata, buf, len >> 2); //写入NFDATA寄存器,写入长度为len >> 2,按字写入
/* cleanup any fractional write */
if (len & 3) { // 处理长度非4整数倍情况
buf += len & ~3;
for (; len & 3; len--, buf++) // 按字节写入
writeb(*buf, s3c_regs->nfdata);
}
}
/*
*init入口函数
*/
static int s3c_nand_init(void)
{
struct clk *nand_clk;
int res;
/*1.分配结构体:nand_chip */
s3c_nand_chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
s3c_mtd = &s3c_nand_chip->base.mtd;
/*2.获取nand flash 寄存器虚拟地址*/
s3c_regs = ioremap(0x4E000000, sizeof(struct nand_regs));
/*3.设置mtd_info*/
s3c_mtd->owner = THIS_MODULE;
s3c_mtd->priv = s3c_nand_chip; //私有数据
/*4.设置nand_chip*/
s3c_nand_chip->legacy.IO_ADDR_R = &s3c_regs->nfdata; //设置读data
s3c_nand_chip->legacy.IO_ADDR_W = &s3c_regs->nfdata; //设置写data
s3c_nand_chip->legacy.select_chip = s3c_nand_select_chip; //片选/取消片选
s3c_nand_chip->legacy.cmd_ctrl = s3c_nand_cmd_ctrl;
s3c_nand_chip->legacy.dev_ready = s3c_nand_devready;
s3c_nand_chip->legacy.read_buf = s3c_nand_read_buf;
s3c_nand_chip->legacy.write_buf = s3c_nand_write_buf;
s3c_nand_chip->ecc.mode = NAND_ECC_NONE; //关闭ECC
s3c_nand_chip->legacy.chip_delay = 50;
/*5.设置硬件相关*/
/*5.1使能nand flash 时钟*/
nand_clk = clk_get(NULL, "nand"); // 获取nand时钟,并赋值给GATE(HCLK_NAND, "nand", "hclk", CLKCON, 4, 0, 0)
// CLKCON寄存器bit[4],控制进入Nand FLash控制器模块的HCLK
clk_prepare_enable(nand_clk);
/*5.2设置时序*/
#define TACLS 0 //0nS
#define TWRPH0 1 //15nS
#define TWRPH1 0 //5nS
s3c_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
/*5.3 bit1:关闭片选 bit0:开启nand flash 控制器*/
s3c_regs->nfcont = (1<<1) | (1<<0);
/*6.扫描NAND*/
if (nand_scan(s3c_nand_chip, 1))
{
res = -ENXIO;
goto out;
}
/*7.行MTD设备注册*/
res = mtd_device_register(s3c_mtd, s3c_nand_part, 4);
if(!res)
{
return 0;
}
out:
mtd_device_unregister(s3c_mtd); //MTD设备卸载
iounmap(s3c_regs); //释放Nand Flash控制器寄存器
kfree(s3c_nand_chip); //释放nand_chip
return 0;
}
/*
* exit出口函数
*/
static void s3c_nand_exit(void)
{
mtd_device_unregister(s3c_mtd); //行MTD设备卸载
iounmap(s3c_regs); //释放nand flash寄存器
kfree(s3c_nand_chip); //释放nand_chip
}
module_init(s3c_nand_init);
module_exit(s3c_nand_exit);
MODULE_LICENSE("GPL");
Makefile
KERN_DIR :=/work/sambashare/linux-5.2.8
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += nand_flash_dev.o
linux驱动移植-linux块设备驱动Nand Flash - 大奥特曼打小怪兽 - 博客园 (cnblogs.com)