基于JZ2440的NOR FLASH的驱动程序的实现

本文详细介绍了如何在Linux-3.4.10内核下为JZ2440开发板上的MX29LV800BBTC NOR FLASH编写驱动程序。首先,对MX29LV800BBTC的基本特性和引脚定义进行了说明。接着,概述了驱动程序的一般实现步骤,包括分配map_info结构体、设置结构体参数、调用do_map_probe()函数以及添加分区。最后,展示了驱动程序的前期准备工作,定义了全局结构体和变量。

本文的主要任务是实现JZ2440开发板板载的MX29LV800BBTC这款NOR FLASH驱动。


一、MX29LV800BBTC的简单介绍

这是一块容量为2MB,位宽为16位的NOR FLASH存储芯片,它可以像内存一样直接进行访问,但是不能像内存一样直接写入,它支持XIP。它的原理图如下所示:


它的引脚定义如下:

        LDATA0~LDATA15:数据引脚

        LADDR1~LADDR20:地址引脚

        LnOE:读使能引脚

        LnWE:写使能引脚

        nGCS0:片选引脚


二、驱动程序编写

驱动基于Linux-3.4.10内核编写。

1、一般实现步骤

a、分配一个map_info结构体变量

b、设置这个结构体变量

c、使用:do_map_probe()

d、添加分区

2、驱动程序编写

2.1 前期准备工作,为了方便整个驱动程序的编写,定义了全局结构体,并定义了一个该结构体的全局变量,具体实现如下:

/* 构造一个方便编写驱动程序的结构体 */
struct yl_nor_mtd{
	struct map_info *map_info;			// 定义map_info结构体的指针变量
	struct mtd_info *mtd_info;			// 定义mtd_info结构体的指针变量
};

/* 定义一个yl_nor_mtd结构体的全局变量 */
static struct yl_nor_mtd nor_mtd;
2.2 分配一个map_info结构体的指针变量

/* 1、分配一个map_info结构体变量 */
	nor_mtd.map_info = kzalloc(sizeof(struct map_info), GFP_KERNEL);

2.3 设置这个结构体变量

	/* 2、设置 */
	nor_mtd.map_info->name 		= "yl_nor_flash";		// 名字
	nor_mtd.map_info->phys 		= 0;					// 物理基地址
	nor_mtd.map_info->size 		= 0x200000; 			// 尺寸,>=真实大小
	nor_mtd.map_info->bankwidth = 2;					// 位宽
	/* 设置虚拟地址的基地址 */
	nor_mtd.map_info->virt 		= ioremap(nor_mtd.map_info->phys, nor_mtd.map_info->size);
	if (nor_mtd.map_info->virt == NULL) 
	{
		printk("Failed to ioremap flash region\n");
		ret = -EIO;
		goto out2;
	}

	/* 其他的nor flash的通用的初始化 */
	simple_map_init(nor_mtd.map_info);
2.4 使用这个结构体变量,获得一个mtd_info结构体变量的实例

	/* 3、使用 */
	nor_mtd.mtd_info = do_map_probe("cfi_probe", nor_mtd.map_info);
	if(nor_mtd.mtd_info == NULL)
	{
		printk("do_map_probe for cfi_probe failure!\n");
		ret = -ENXIO;
		goto out3;
	}
2.5 添加分区

/* 4、添加分区 */
	ret = mtd_device_parse_register(nor_mtd.mtd_info, NULL, NULL,
				yl_s3c_nor_partitions, ARRAY_SIZE(yl_s3c_nor_partitions));
添加分区需要有一个分区表作为分区依据,分区表的实现如下所示:

/* 定义nor flash的分区表 */
static struct mtd_partition yl_s3c_nor_partitions[] = 
{
	[0] = {
		.name	= "bootloader_nor",
		.size	= SZ_256K,
		.offset	= 0,
	},
	[1] = {
		.name	= "params_nor",
		.offset = MTDPART_OFS_APPEND,
		.size	= SZ_128K,
	},
	[2] = {
		.name	= "rootfs_nor",
		.offset	= MTDPART_OFS_APPEND,
		.size	= MTDPART_SIZ_FULL,
	}
};


整个驱动的完整代码如下所示:
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/concat.h>
#include <linux/io.h>

/* 构造一个方便编写驱动程序的结构体 */
struct yl_nor_mtd{
	struct map_info *map_info;			// 定义map_info结构体的指针变量
	struct mtd_info *mtd_info;			// 定义mtd_info结构体的指针变量
};

/* 定义一个yl_nor_mtd结构体的全局变量 */
static struct yl_nor_mtd nor_mtd;

/* 定义nor flash的分区表 */
static struct mtd_partition yl_s3c_nor_partitions[] = 
{
	[0] = {
		.name	= "bootloader_nor",
		.size	= SZ_256K,
		.offset	= 0,
	},
	[1] = {
		.name	= "params_nor",
		.offset = MTDPART_OFS_APPEND,
		.size	= SZ_128K,
	},
	[2] = {
		.name	= "rootfs_nor",
		.offset	= MTDPART_OFS_APPEND,
		.size	= MTDPART_SIZ_FULL,
	}
};


/* 模块的入口函数 */
static int __init yl_s3c_nor_init(void)
{
	int ret = 0;

	/* 1、分配一个map_info结构体变量 */
	nor_mtd.map_info = kzalloc(sizeof(struct map_info), GFP_KERNEL);
	if (!nor_mtd.map_info) {
		printk("kzalloc for map_info error!\n");
		ret = -ENOMEM;
		goto out1;
	}

	/* 2、设置 */
	nor_mtd.map_info->name 		= "yl_nor_flash";		// 名字
	nor_mtd.map_info->phys 		= 0;					// 物理基地址
	nor_mtd.map_info->size 		= 0x200000; 			// 尺寸,>=真实大小
	nor_mtd.map_info->bankwidth = 2;					// 位宽
	/* 设置虚拟地址的基地址 */
	nor_mtd.map_info->virt 		= ioremap(nor_mtd.map_info->phys, nor_mtd.map_info->size);
	if (nor_mtd.map_info->virt == NULL) 
	{
		printk("Failed to ioremap flash region\n");
		ret = -EIO;
		goto out2;
	}

	/* 其他的nor flash的通用的初始化 */
	simple_map_init(nor_mtd.map_info);

	/* 3、使用 */
	nor_mtd.mtd_info = do_map_probe("cfi_probe", nor_mtd.map_info);
	if(nor_mtd.mtd_info == NULL)
	{
		printk("do_map_probe for cfi_probe failure!\n");
		ret = -ENXIO;
		goto out3;
	}

	/* 4、添加分区 */
	ret = mtd_device_parse_register(nor_mtd.mtd_info, NULL, NULL,
				yl_s3c_nor_partitions, ARRAY_SIZE(yl_s3c_nor_partitions));
	if(ret)	// 失败
	{
		printk("mtd_device_parse_register error!\n");
		ret = -ENODEV;
		goto out3;
	}
	else	// 成功
	{
		return 0;	
	}
	
out3:
	iounmap(nor_mtd.map_info->virt);
out2:
	kfree(nor_mtd.map_info);
out1:
	return ret;
}

/* 模块的出口函数 */
static void __exit yl_s3c_nor_exit(void)
{
	/* 进行出口的一些操作,释放资源,解除绑定,取消注册 */
	mtd_device_unregister(nor_mtd.mtd_info);
	iounmap(nor_mtd.map_info->virt);
	kfree(nor_mtd.map_info);
}

module_init(yl_s3c_nor_init);
module_exit(yl_s3c_nor_exit);

MODULE_LICENSE("GPL");

3、编译驱动程序并把模块加载进内核,结果如下所示:



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值