/**
* blk_init_queue - prepare a request queue for use with a block device
-----给块设备提供一个请求队列使用
* @rfn: The function to be called to process requests that have been
* placed on the queue.
-----放入队列的请求使用该函数处理
* @lock: Request queue spin lock
*
-----请求队列的自旋锁
* Description:
* If a block device wishes to use the standard request handling procedures,
* which sorts requests and coalesces adjacent requests, then it must
* call blk_init_queue().
-------块设备需要使用标准的请求处理流程时(排序请求和合并相邻请求)需要调用blk_init_queue
The function @rfn will be called when there
* are requests on the queue that need to be processed.
If the device
* supports plugging, then @rfn may not be called immediately when requests are available on the queue, but may be called at some time later instead.
------如果设备支持插拔,当队列中的请求可以使用的时候,并不会立即就调用rfn函数,而是过一段时间才调用
* Plugged queues are generally unplugged when a buffer belonging to one
* of the requests on the queue is needed, or due to memory pressure.
*
* @rfn is not required, or even expected, to remove all requests off the
* queue, but only as many as it can handle at a time. If it does leave
* requests on the queue, it is responsible for arranging that the requests
* get dealt with eventually.
*
* The queue spin lock must be held while manipulating the requests on the
* request queue; this lock will be taken also from interrupt context, so irq
* disabling is needed for it.
*
* Function returns a pointer to the initialized request queue, or NULL if
* it didn't succeed.
------执行成功,函数返回指向这个request queue,不成功的话,返回空指针
* Note:
* blk_init_queue() must be paired with a blk_cleanup_queue() call
------------这两个函数必须配对使用
* when the block device is deactivated (such as at module unload).
**/
程序源码,重点查看init函数有详细讲解
/* :
* drivers\block\xd.c
* drivers\block\z2ram.c
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/blkdev.h>
#include <linux/blkpg.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/dma.h>
#define SIZE (1024)
static struct gendisk *ram_gendisk = NULL;
static struct request_queue *ram_queue = NULL;
static DEFINE_SPINLOCK(ram_lock);
static int major;
static char *buf = NULL;
static void do_queue_request(request_queue_t *q)
{
struct request *req;
static int cnt = 0;
while ((req = elv_next_request(q)) != NULL) {
unsigned long start = req->sector << 9;
unsigned long len = req->current_nr_sectors << 9;
if (start + len > (SIZE * 512)) {
printk( KERN_ERR ": bad access: block=%lu, count=%u\n",
req->sector, req->current_nr_sectors);
end_request(req, 0);
continue;
}
if (READ == rq_data_dir(req)) {
memcpy(req->buffer, (const void *)(buf+start), len);
} else {
memcpy(buf+start, (const void *)req->buffer, len);
}
end_request(req, 1);
printk("func:%s, line:%d, cnt:%d, R(0)W(1):%d, start:%x, len:%x, sector:%x, nr:%x\n",
__func__, __LINE__, cnt++, rq_data_dir(req), start, len, req->sector,
req->current_nr_sectors);
}
}
static struct block_device_operations ram_fops =
{
.owner = THIS_MODULE,
};
static int ramblock_init(void)
{
/*
1.分配一个gendisk结构体
2.设置一个队列,将我们的请求放到队列里去
3.设置这个gnedisk结构体的属性,如容量等
4.add_gendisk函数
5.另外需要自己分配一块内存空间用来当做块设备,在request函数中memcpy访问
他,模仿块设备读写
--------怎么进行测试
1. insmod ramblock.ko
2. 格式化: mkdosfs /dev/ramblock
3. 挂接: mount /dev/ramblock /tmp/
4. 读写文件: cd /tmp, 在里面vi文件
5. cd /; umount /tmp/
6. cat /dev/ramblock > /mnt/ramblock.bin
7. 在PC上查看ramblock.bin
sudo mount -o loop ramblock.bin /mnt
*/
major = register_blkdev(0, "ramblock");
if (major < 0) {
printk("fail register block device!\n");
}
ram_gendisk = alloc_disk(16);
if (!ram_gendisk){
printk("alloc disk fail!\n");
return -1;
}
ram_queue = blk_init_queue(do_queue_request, &ram_lock);
if (!ram_queue) {
printk("queue is init fail!\n");
return -1;
}
buf = kzalloc(SIZE * 512, GFP_KERNEL);
if (!buf) {
printk("alloc failed!\n");
return -1;
}
ram_gendisk->major = major;
ram_gendisk->first_minor = 0;
ram_gendisk->fops = &ram_fops;
sprintf(ram_gendisk->disk_name, "ramblock");
ram_gendisk->queue = ram_queue;
set_capacity(ram_gendisk, SIZE);
add_disk(ram_gendisk);
return 0;
}
static void ramblock_exit(void)
{
if (unregister_blkdev(major, "ramblock")) {
printk( KERN_ERR ": unregister of device failed\n");
}
del_gendisk(ram_gendisk);
put_disk(ram_gendisk);
blk_cleanup_queue(ram_queue);
}
module_init(ramblock_init);
module_exit(ramblock_exit);
MODULE_LICENSE("GPL");
此时还不能进行创建分区,使用fdisk命令的时候会有错误
需要在fp_ops中添加getgeo函数
# fdisk /dev/ramblock
func:do_queue_request, line:60, cnt:182, R(0)W(1):0, start:0, len:1000, sector:8, nr:8
func:do_queue_request, line:60, cnt:183, R(0)W(1):0, start:1000, len:1000, sector:10, nr:8
func:do_queue_request, line:60, cnt:184, R(0)W(1):0, start:2000, len:1000, sector:18, nr:8
func:do_queue_request, line:60, cnt:185, R(0)W(1):0, start:3000, len:1000, sector:18, nr:8
Unknown value(s) for: cylinders (settable in the extra functions menu)
使用n命令创建分区,最终使用w保存分区,保存分区表,最终可以单独格式化或挂接其中一个分区
nand falsh设备驱动 K9F2G08U0C
NFCONF 0x4E000000
NFCMMD 0x4E000008
NFADDR 0x4E00000C
NFDATA 0x4E000010
- 命令集
5.3 Copy-back Program
Copy-Back program with Read for Copy-Back is configured to quickly and efficiently rewrite data stored in one page without data re-loading when the bit error is not in data stored.
the bit error is not in data stored,存在页里的数据快速重写,没有了re-loading的时间,
Since the time-consuming re-loading cycles are removed, the system performance is improved. The benefit is especially obvious when a portion of a block is updated and the rest of the block also needs to be copied to the newly assigned free block. Copy-Back operation is a sequential execution of Read for Copy-Back and of copy-back program with the destination page address. A read operation with "35h" command and the
address of the source page moves the whole 2,112-byte data into the internal data buffer. A bit error is checked by sequential reading the data output. In
the case where there is no bit error, the data do not need to be reloaded. Therefore Copy-Back program operation is initiated by issuing Page-Copy DataInput command (85h) with destination page address. Actual programming operation begins after Program Confirm command (10h) is issued. Once the
program process starts, the Read Status Register command (70h) may be entered to read the status register. The system controller can detect the completion of a program cycle by monitoring the R/B output, or the Status bit(I/O 6) of the Status Register. When the Copy-Back Program is complete, the
Write Status Bit(I/O 0) may be checked(Figure 8 & Figure 9). The command register remains in Read Status command mode until another valid command is written to the command register.
During copy-back program, data modification is possible using random data input command (85h) as shown in Figure 9
1.选中芯片
读取NFCONT,修改使能那几位
OpenJTAG> md.b 0x4e000004 1
4e000004: 03
修改为0x1,使能nand
mw.b 0x4e000004 0x1
2.发出命令
mw.b 0x4e000008 0x90
3.发出地址
mw.b 0x4e00000c 0x0
4.读取数据,读取数据操作必须是1个字节,一个字节读取才能读取到正确数据,连续读取5个周期,不能一次性读取5个字节,这样得出来的数据不对
md.b 0x4e000010 1
读取数据操作,打开u-boot.bin(或者使用命令nand dump 0)对照获取回来的数据是否准确,同样发现获取数据时只能1次,可能因为数据线只有8根的原因
mw.b 0x4E000004 1
mw.b 0x4E000008 0x00
mw.b 0x4E00000C 0x00
mw.b 0x4E00000C 0x00
mw.b 0x4E00000C 0x00
mw.b 0x4E00000C 0x00
mw.b 0x4E00000C 0x00
mw.b 0x4E000008 0x30
md.b 0x4E000010 1
写好的源代码
/*
* drivers\mtd\nand\s3c2410.c
* drivers\mtd\nand\at91_nand.c
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.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/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/arch/regs-nand.h>
#include <asm/arch/nand.h>
static struct nand_chip *s3c_nand_chip = NULL;
struct s3c_nand_regs {
unsigned long nfconf ;
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 ;
};
#define S3C2410_NFSTAT_BUSY (1<<0)
#define NAND_SELECT (0x1)
#define NAND_DISELECT (0x3)
#define TACLS_MASK (0x3 << 12)
#define TWRPH0_MASK (0x7 << 8)
#define TWRPH1_MASK (0x7 << 4)
#define TACLS (0x1 << 12)
#define TWRPH0 (0x1 << 8)
#define TWRPH1 (0x1 << 4)
static struct s3c_nand_regs *nand_reg = NULL;
static struct mtd_info *s3c_mtd_info = NULL;
static struct clk *nand_clk = NULL;
static struct mtd_partition my_nand_part[] = {
[0] = {
.name = "bootloader",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "params",
.offset = MTDPART_OFS_APPEND,
.size = 0x00020000,
},
[2] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x00200000,
},
[3] = {
.name = "root",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
static int mynand_ready(struct mtd_info *mtd)
{
return (nand_reg->nfstat & S3C2410_NFSTAT_BUSY);
}
static void mynand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
if (NAND_CMD_NONE == cmd)
return;
if (ctrl & NAND_CLE) {
writeb(cmd, &nand_reg->nfcmd);
} else if (ctrl & NAND_ALE) {
writeb(cmd, &nand_reg->nfaddr);
}
}
static void mynand_select_chip(struct mtd_info *mtd, int chip)
{
switch (chip) {
case -1:
nand_reg->nfcont &= NAND_DISELECT;
break;
case 0:
nand_reg->nfcont &= NAND_SELECT;
break;
default:
BUG();
}
}
static int __init nand_init(void)
{
/*
1.分配一个nand_chip结构体,select_chip,实现操作底层硬件的函数,设置
1.1 芯片是8位的需要进行设置options为0
1.2 select函数,-1表示不选中,0表示选中
1.3 cmd_ctrl函数,写命令的函数
1.4 dev_ready,芯片是否正在忙
1.5 读写函数的地址需要告诉内核
1.6 开启ECC
2.分配设置mtd
3.硬件时序上的设置,设置硬件的时序
4.add_mtd_partitions
*/
s3c_nand_chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
if (!s3c_nand_chip) {
printk(KERN_ERR "nand: failed to allocate chip structure.\n");
return -ENOMEM;
}
#if 0
nand_scan
nand_scan_ident
nand_set_defaults
nand_select_chip
nand_command
nand_get_flash_type
#endif
nand_reg = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
if (!nand_reg) {
printk("func:%s, line:%d failed to ioremap!\n",
__func__, __LINE__);
}
s3c_nand_chip->options = 0;
s3c_nand_chip->select_chip = mynand_select_chip;
s3c_nand_chip->cmd_ctrl = mynand_cmd_ctrl;
s3c_nand_chip->dev_ready = mynand_ready;
s3c_nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */
s3c_nand_chip->IO_ADDR_R = &nand_reg->nfdata;
s3c_nand_chip->IO_ADDR_W = &nand_reg->nfdata;
s3c_mtd_info = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
if (!s3c_mtd_info) {
printk(KERN_ERR "nand: failed to allocate mtd_info structure.\n");
return -ENOMEM;
}
s3c_mtd_info->priv = s3c_nand_chip;
s3c_mtd_info->owner = THIS_MODULE;
nand_reg->nfconf &= ~(TACLS_MASK | TWRPH0_MASK | TWRPH1_MASK);
nand_reg->nfconf |= (TACLS | TWRPH0 | TWRPH1);
#if 0
nand_clk = clk_get(NULL, "nand");
if (IS_ERR(nand_clk) ) {
printk(KERN_ERR "S3C2440: Failed to get parent clocks\n");
return -EINVAL;
}
if (clk_enable(nand_clk)) {
printk("func:%s, line:%d\n", __func__, __LINE__);
}
#endif
nand_scan(s3c_mtd_info, 1);
if (add_mtd_partitions(s3c_mtd_info, my_nand_part, 4)) {
printk("func:%s, line:%d, can't add mtd partitions\n", __func__, __LINE__);
return -ENOMEM;
}
return 0;
}
static void __exit nand_exit(void)
{
kfree(s3c_mtd_info);
kfree(s3c_nand_chip);
iounmap(nand_reg);
del_mtd_partitions(s3c_mtd_info);
}
module_init(nand_init);
module_exit(nand_exit);
MODULE_LICENSE("GPL");