17.NOR FLASH驱动
NOR FLASH硬件原理参考:https://blog.csdn.net/qq_16933601/article/details/102653367
一、内核NOR FLASH驱动框架分析
1.physmap_init
static int __init physmap_init(void)
{
int err;
err = platform_driver_register(&physmap_flash_driver);
#ifdef PHYSMAP_COMPAT
if (err == 0)
platform_device_register(&physmap_flash);
#endif
return err;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
2.physmap_flash
static struct platform_device physmap_flash = {
.name = "physmap-flash",
.id = 0,
.dev = {
.platform_data = &physmap_flash_data,
},
.num_resources = 1,
.resource = &physmap_flash_resource,
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
3.physmap_flash_resource
static struct resource physmap_flash_resource = {
.start = CONFIG_MTD_PHYSMAP_START,//nor flash的物理基地址
.end = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,//nor flash的容量长度
.flags = IORESOURCE_MEM,
};
- 1
- 2
- 3
- 4
- 5
4.physmap_flash_driver
static struct platform_driver physmap_flash_driver = {
.probe = physmap_flash_probe,
.remove = physmap_flash_remove,
#ifdef CONFIG_PM
.suspend = physmap_flash_suspend,
.resume = physmap_flash_resume,
.shutdown = physmap_flash_shutdown,
#endif
.driver = {
.name = "physmap-flash",
},
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
注册平台,平台devices里面含有配置信息,基地址,位宽,长度等
5.physmap_flash_probe
static int physmap_flash_probe(struct platform_device *dev)
{
struct physmap_flash_data *physmap_data;
struct physmap_flash_info *info;
const char **probe_type;
int err;
......................................
/*1. 分配结构体*/
info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);
/*2.设置map_info 结构体*/
info->map.name = dev->dev.bus_id;//norflash的名字
info->map.phys = dev->resource->start;//物理基地址
info->map.size = dev->resource->end - dev->resource->start + 1;//容量长度
info->map.bankwidth = physmap_data->width;//nor flash的字节位宽
info->map.set_vpp = physmap_data->set_vpp;//虚拟地址
info->map.virt = ioremap(info->map.phys, info->map.size);//映射物理地址
simple_map_init(&info->map);//简单初始化map_info的其它成员
/*3. 设置mtd_info 结构体 */
/*通过probe_type指向的名称来识别芯片,当do_map_probe()函数返回NULL表示没找到*/
/*当找到对应的芯片mtd_info结构体,便返回给当前的info->mtd */
probe_type = rom_probe_types;
info->mtd = do_map_probe(*probe_type, &info->map);//通过do_map_probe ()来识别芯片
info->mtd->owner = THIS_MODULE;
add_mtd_partitions(info->mtd, info->parts, err);
if (info->mtd == NULL) { //最终还是没找到芯片,便注销之前注册的东西并退出
dev_err(&dev->dev, "map_probe failed\n");
err = -ENXIO;
goto err_out;
}
info->mtd->owner = THIS_MODULE;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
6.rom_probe_types
如何添加分区 ,仿照nor flash 数组划分分区
static struct mtd_partition s3c_nand_parts[] = {
[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,//剩下的所有
}
};
//添加分区
add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);
err_out:
physmap_flash_remove(dev); //该函数用来注销之前注册的东西
return err;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
通过上面的代码和注释分析到,和我们上一节的nand flash驱动相似,这里是map_info结构体和mtd_info结构体来完成的,当我们要对nor flash分区就要使用add_mtd_partitions()才行
其中当*probe=="cfi_probe"时:
就会通过do_map_probe(“cfi_probe”,&info->map)来识别芯片
最终会进入drivers/mtd/chips/cfi_probe.c的cfi_probe_chip()函数来进入cfi模式,读取芯片信息
当*probe_type=="jedec_probe"时:
最终会进入drivers/mtd/chips/jedec_probe.c中的jedec_probe_chip()函数来使用读ID命令,通过ID来匹配jedec_table[]数组。
所以注册一个块设备驱动,需要以下步骤:
-
分配mtd_info 结构体和map_info 结构体
-
设置map_info 结构体
-
设置mtd_info 结构体
-
使用add_mtd_partitions()或者add_mtd_device()来创建MTD字符/块设备
7.看下如何识别do_map_probe
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);
/* We decrease the use count here. It may have been a
probe-only module, which is no longer required from this
point, having given us a handle on (and increased the use
count of) the actual driver code.
*/
module_put(drv->module);
if (ret)
return ret;
return NULL;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
8.get_mtd_chip_driver
static struct mtd_chip_driver *get_mtd_chip_driver (const char *name)
{
struct list_head *pos;
struct mtd_chip_driver *ret = NULL, *this;
spin_lock(&chip_drvs_lock);
list_for_each(pos, &chip_drvs_list) {//链表存放的chip_drvs_list
this = list_entry(pos, typeof(*this), list);
if (!strcmp(this->name, name)) {
ret = this;
break;
}
}
if (ret && !try_module_get(ret->module))
ret = NULL;
spin_unlock(&chip_drvs_lock);
return ret;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
9.谁设置list_for_each链表呢
void register_mtd_chip_driver(struct mtd_chip_driver *drv)
{
spin_lock(&chip_drvs_lock);
list_add(&drv->list, &chip_drvs_list);
spin_unlock(&chip_drvs_lock);
}
- 1
- 2
- 3
- 4
- 5
- 6
10.谁来调用register_mtd_chip_driver注册呢
cfi_probe.c
static int __init cfi_probe_init(void)
{
register_mtd_chip_driver(&cfi_chipdrv);
return 0;
}
- 1
- 2
- 3
- 4
- 5
11.看下cfiprobe 做了什么
mtd_do_chip_probe
struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp)
{
struct mtd_info *mtd = NULL;
struct cfi_private *cfi;
/* First probe the map to see if we have CFI stuff there. */
cfi = genprobe_ident_chips(map, cp);//通用枚举
if (!cfi)
return NULL;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
12.genprobe_ident_chips
static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
{
if (!genprobe_new_chip(map, cp, &cfi)) {
- 1
- 2
- 3
13.genprobe_new_chip
static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
struct cfi_private *cfi)
{
int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */
int max_chips = map_bankwidth(map); /* And minimum 1 */
int nr_chips, type;
for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) {
if (!cfi_interleave_supported(nr_chips))
continue;
cfi->interleave = nr_chips;
/* Minimum device size. Don't look for one 8-bit device
in a 16-bit bus, etc. */
type = map_bankwidth(map) / nr_chips;
for (; type <= CFI_DEVICETYPE_X32; type<<=1) {
cfi->device_type = type;
if (cp->probe_chip(map, 0, NULL, cfi))//cfi_chip_probe
return 1;
}
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
static struct chip_probe cfi_chip_probe = {
.name = "CFI",
.probe_chip = cfi_probe_chip
};
- 1
- 2
- 3
- 4
14.cfi_probe_chip
static int __xipram cfi_probe_chip(struct map_info *map, __u32 base,
unsigned long *chip_map, struct cfi_private *cfi)
{
xip_disable();
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); // 进入CFI模式
cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
if (!qry_present(map,base,cfi)) {
xip_enable(base, map, cfi);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
15.qry_present
static int __xipram qry_present(struct map_info *map, __u32 base,// // 看是否能读出"QRY"
struct cfi_private *cfi)
{
int osf = cfi->interleave * cfi->device_type; // scale factor
map_word val[3];
map_word qry[3];
qry[0] = cfi_build_cmd('Q', map, cfi);
qry[1] = cfi_build_cmd('R', map, cfi);
qry[2] = cfi_build_cmd('Y', map, cfi);
val[0] = map_read(map, base + osf*0x10);//读地址返回的值
val[1] = map_read(map, base + osf*0x11);
val[2] = map_read(map, base + osf*0x12);
if (!map_word_equal(map, qry[0], val[0]))
return 0;
if (!map_word_equal(map, qry[1], val[1]))
return 0;
if (!map_word_equal(map, qry[2], val[2]))
return 0;
return 1; // "QRY" found
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
16.do_map_probe(“jedec_probe”, s3c_nor_map);
drv = get_mtd_chip_driver(name)
ret = drv->probe(map); // jedec_probe
static struct mtd_info *jedec_probe(struct map_info *map)
{
/*
* Just use the generic probe stuff to call our CFI-specific
* chip_probe routine in all the possible permutations, etc.
*/
return mtd_do_chip_probe(map, &jedec_chip_probe);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp)
{
struct mtd_info *mtd = NULL;
struct cfi_private *cfi;
/* First probe the map to see if we have CFI stuff there. */
cfi = genprobe_ident_chips(map, cp);//通用枚举
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
17.genprobe_ident_chips
static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
{
struct cfi_private cfi;
struct cfi_private *retcfi;
unsigned long *chip_map;
int i, j, mapsize;
int max_chips;
memset(&cfi, 0, sizeof(cfi))
if (!genprobe_new_chip(map, cp, &cfi)) {
/* The probe didn't like it */
printk(KERN_DEBUG "%s: Found no %s device at location zero\n",
cp->name, map->name);
return NULL;
cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
static struct chip_probe jedec_chip_probe = {
.name = "JEDEC",
.probe_chip = jedec_probe_chip
};
- 1
- 2
- 3
- 4
18.jedec_probe_chip最后识别
static int jedec_probe_chip(struct map_info *map, __u32 base,
unsigned long *chip_map, struct cfi_private *cfi)
{
int i;
enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED;
u32 probe_offset1, probe_offset2;
retry:
if (!cfi->numchips) {
uaddr_idx++;
if (MTD_UADDR_UNNECESSARY == uaddr_idx)
return 0;
cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1;
cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2;
}
/* Make certain we aren't probing past the end of map */
if (base >= map->size) {
printk(KERN_NOTICE
"Probe at base(0x%08x) past the end of the map(0x%08lx)\n",
base, map->size -1);
return 0;
// 解锁
cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL);
// 读ID命令
cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
// 得到厂家ID,设备ID
cfi->mfr = jedec_read_mfr(map, base, cfi);
cfi->id = jedec_read_id(map, base, cfi);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
19.jedec_table
static const struct amd_flash_info jedec_table[] = {
{
.mfr_id = MANUFACTURER_AMD,
.dev_id = AM29F032B,
.name = "AMD AM29F032B",
.uaddr = {
[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
},
.DevSize = SIZE_4MiB,
.CmdSet = P_ID_AMD_STD,
.NumEraseRegions= 1,
.regions = {
ERASEINFO(0x10000,64)
}
}, {
.mfr_id = MANUFACTURER_AMD,
.dev_id = AM29LV160DT,
.name = "AMD AM29LV160DT",
.uaddr = {
[0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
},
.DevSize = SIZE_2MiB,
.CmdSet = P_ID_AMD_STD,
.NumEraseRegions= 4,
.regions = {
ERASEINFO(0x10000,31),//擦除块大小 不同区域擦出快大小不一样
ERASEINFO(0x08000,1),
ERASEINFO(0x02000,2),
ERASEINFO(0x04000,1)
}
// 最后和数组比较
jedec_table
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
注意: 关于NOR FLASH和2440地址线错位相接:
2440或2410中地址线都是从ADDR2中开始连接的,即A0-ADDR2、An- ADDR(n+2)、这是因为ARM是32位处理器所以它一次处理数据都是以32位为单位的,也就是说它读或者写数据时,地址只能为0x0000_0000、0x0000_0004、0x0000_0008。。。即4字节对齐,因为一般DDR的数据线都为16位,所以为了得到32位的数据,一般都是将2个DDR连在一起,它们的地址相同,所以对已DDR而言是一个地址对应4个字节(因为一个DDR对应2个字节,两个DDR就对因4个字节),但是对于CPU而言一个地址只对应1个字节,所以这里就存在一个地址转换问题,即如何让1字节的地址和4字节的地址对应起来。其实就是让CPU发出的4次地址都取到同一份数据就可以了(0地址对应的是DDR的0地址,1地址对应的是DDR的0地址,2地址对应的还是DDR的0地址,3地址对应的还是DDR的0地址,虽然都是0地址,但是0,1,2,3对应的其实是0地址的内部的不同部分,这部分是是由Memory Controller来控制的,根据0,1,2,3选择0地址内部不同的部分返回给CPU)。即使CPU的0~ 3地址里的数据对应DDR的0地址数据,CPU的4~7地址的数据对应DDR的1地址的数据,所以CPU的0地址对应DDR的0地址,0X04地址对应DDR中1地址,0x08地址对应DDR中2地址,可以看出,DDR的地址刚好是CPU寻址向右移动两位,所以2440或2410中地址线都是从ADDR2开始连接的。
http://bbs.100ask.net/question/8202
http://bbs.100ask.net/question/9740
http://bbs.100ask.net/question/11301
http://bbs.100ask.net/question/10963
https://www.cnblogs.com/losing-1216/p/4885588.html
二、写代码
/*
* 参考 drivers\mtd\maps\physmap.c
*/
#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 <asm/io.h>
static struct map_info *s3c_nor_map;
static struct mtd_info *s3c_nor_mtd;
static struct mtd_partition s3c_nor_parts[] = {
[0] = {
.name = "bootloader_nor",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "root_nor",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
static int s3c_nor_init(void)
{
/* 1. 分配map_info结构体 */
s3c_nor_map = kzalloc(sizeof(struct map_info), GFP_KERNEL);;
/* 2. 设置: 物理基地址(phys), 大小(size), 位宽(bankwidth), 虚拟基地址(virt) */
s3c_nor_map->name = "s3c_nor";
s3c_nor_map->phys = 0;
s3c_nor_map->size = 0x1000000; /* >= NOR的真正大小 */
s3c_nor_map->bankwidth = 2;
s3c_nor_map->virt = ioremap(s3c_nor_map->phys, s3c_nor_map->size);
simple_map_init(s3c_nor_map);
/* 3. 使用: 调用NOR FLASH协议层提供的函数来识别 */
printk("use cfi_probe\n");
s3c_nor_mtd = do_map_probe("cfi_probe", s3c_nor_map);
if (!s3c_nor_mtd)
{
printk("use jedec_probe\n");
s3c_nor_mtd = do_map_probe("jedec_probe", s3c_nor_map);
}
if (!s3c_nor_mtd)
{
iounmap(s3c_nor_map->virt);
kfree(s3c_nor_map);
return -EIO;
}
/* 4. add_mtd_partitions */
add_mtd_partitions(s3c_nor_mtd, s3c_nor_parts, 2);
return 0;
}
static void s3c_nor_exit(void)
{
del_mtd_partitions(s3c_nor_mtd);
iounmap(s3c_nor_map->virt);
kfree(s3c_nor_map);
}
module_init(s3c_nor_init);
module_exit(s3c_nor_exit);
MODULE_LICENSE("GPL");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
文章来源: blog.csdn.net,作者:嵌入式与Linux那些事,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/qq_16933601/article/details/103623919
- 点赞
- 收藏
- 关注作者
评论(0)