/**
* 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.
-----放入隊列的請求使用該函數(shù)處理
* @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函數(shù),而是過一段時間才調用
* 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.
------執(zhí)行成功,函數(shù)返回指向這個request queue,不成功的話,返回空指針
* Note:
* blk_init_queue() must be paired with a blk_cleanup_queue() call
------------這兩個函數(shù)必須配對使用
* when the block device is deactivated (such as at module unload).
**/
程序源碼,重點查看init函數(shù)有詳細講解
/* :
* 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函數(shù)
5.另外需要自己分配一塊內存空間用來當做塊設備,在request函數(shù)中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");
此時還不能進行創(chuàng)建分區(qū),使用fdisk命令的時候會有錯誤
需要在fp_ops中添加getgeo函數(shù)
# 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命令創(chuàng)建分區(qū),最終使用w保存分區(qū),保存分區(qū)表,最終可以單獨格式化或掛接其中一個分區(qū)
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,存在頁里的數(shù)據(jù)快速重寫,沒有了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.發(fā)出命令
mw.b 0x4e000008 0x90
3.發(fā)出地址
mw.b 0x4e00000c 0x0
4.讀取數(shù)據(jù),讀取數(shù)據(jù)操作必須是1個字節(jié),一個字節(jié)讀取才能讀取到正確數(shù)據(jù),連續(xù)讀取5個周期,不能一次性讀取5個字節(jié),這樣得出來的數(shù)據(jù)不對
md.b 0x4e000010 1
讀取數(shù)據(jù)操作,打開u-boot.bin(或者使用命令nand dump 0)對照獲取回來的數(shù)據(jù)是否準確,同樣發(fā)現(xiàn)獲取數(shù)據(jù)時只能1次,可能因為數(shù)據(jù)線只有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,實現(xiàn)操作底層硬件的函數(shù),設置
1.1 芯片是8位的需要進行設置options為0
1.2 select函數(shù),-1表示不選中,0表示選中
1.3 cmd_ctrl函數(shù),寫命令的函數(shù)
1.4 dev_ready,芯片是否正在忙
1.5 讀寫函數(shù)的地址需要告訴內核
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");