您现在的位置是:首页 > 软件 > Linux SPI Flash 驱动
推荐星级:
  • 1
  • 2
  • 3
  • 4
  • 5

Linux SPI Flash 驱动

更新时间:2019-09-07 16:13:58 大小:2M 上传用户:tang190119查看TA发布的资源 标签:linuxspiflash驱动 下载积分:2分 评价赚积分 (如何评价?) 收藏 评论(0) 举报

资料介绍

新出的SPI FLASH,如何添加到linux 内核

部分文件列表

文件名 大小
Linux_驱动_SPI_Flash.pdf 2M

部分页面预览

(完整内容请下载后查看)
疯狂Python精讲  
关注  
Linux 驱动 SPI Flash(W25Q80DV)  
2018年06月13日 19:21:27 阅读数 4851 更多  
粉丝  
喜欢  
评论  
1049  
1027  
488  
版权声明:开心源自分享,快乐源于生活 —— 分享技术,传递快乐。转载文章请注明出处,谢谢!
如果还没看 W25Q80DV 的数据手册,赶紧去看!  
等级:  
访问: 135万+  
排名: 1572  
积分: 1万+  
勋章:  
ꢀꢀ本文描述的是在 i.MX6q 硬件平台上添加 W25Q80DV 芯片(SPI 设备),Linux 内核版本为 kernel-3.10.17,采用 DeviceTree 描  
述硬件连接信息。  
关于我  
博主:90后大叔 - 卢小喵  
硬件连接  
学历:宇宙工业大学 - 电气工程及其自动化  
领域:在工控和通信行业摸爬滚打好几年,  
对物联网、Web 技术略知一二,混迹于 IT  
圈、环保圈、健身圈、创业圈...... 梦想有一  
份对人类有益的小而美的事业!  
ꢀꢀi.MX6q 是基于 NXP 四核 ARM Cortex-A9 架构的高性能处理器,它上面有 5 个 SPI 控制器,分别是 ECSPI1~5。在我们这里的测  
试平台上的硬件连接的情况是这样的:  
邮箱:  
微信公众号  
我是一名软件工程师,也是一名零废弃倡导  
者,如果您跟我一样,把环保当成一种生活  
习惯,把环保当成一种时尚,欢迎关注我的  
个人公众号,一起交流关于技术与生活的所  
思所想。  
ꢀꢀ管脚描述:  
最新文章  
博主专栏  
文章数:23 篇 访问量:5万+  
文章数:17 篇 访问量:6万+  
文章数:2 篇 访问量:6554  
驱动模型  
文章数:5 篇 访问量:1万+  
展开  
个人分类  
展开  
归档  
展开  
MTD  
热门文章  
ꢀꢀMTD设备分为四层(从设备节点直到底层硬件驱动),这四层从上到下依次是:  
阅读数 70342  
设备节点  
阅读数 46228  
MTD设备层  
MTD原始设备层  
硬件驱动层  
阅读数 38515  
ꢀꢀMTD 子系统实现了 SPI flash 芯片驱动程序,其驱动 Demo 为:  
ꢀꢀdrivers/mtd/devices/mtd_dataflash.c  
阅读数 36588  
ꢀꢀdrivers/mtd/devices/m25p80.c  
阅读数 34244  
驱动文件  
最新评论  
ꢀꢀ对于我们这里的 W25Q80DV 设备,重点关注的驱动文件是:  
ꢀꢀdrivers/mtd/devices/m25p80.c  
ꢀꢀ其主要代码框架如下:  
y] 喜大普奔!  
1
static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)  
2
3
如此优秀  
{
4
}
5
载了~  
6
static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)  
7
{
8
9
}
10  
11  
ply] 这个应该怎么操作?  
static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)  
12  
{
13  
14  
}
15  
16  
static const struct spi_device_id m25p_ids[] = {  
17  
#ifdef CONFIG_ARCH_ADVANTECH  
18  
19  
CSDN学院  
CSDN企业招聘  
{ "n25q", INFO(0x20ba16, 0, 64 * 1024, 64, SECT_4K) },  
{ "n25q", INFO(0x20bb16, 0, 64 * 1024, 64, SECT_4K) },  
#endif  
20  
400-660-0108  
21  
22  
工作时间 8:30-22:00  
23  
{ "at25fs010", INFO(0x1f6601, 0, 32 * 1024,  
{ "at25fs040", INFO(0x1f6604, 0, 64 * 1024,  
4, SECT_4K) },  
8, SECT_4K) },  
24  
25  
26  
©1999-2019 北京创新乐知网络技术有限  
公司  
27  
{ "m25p40", INFO(0x202013, 0, 64 * 1024,  
8, 0) },  
28  
{ "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) },  
29  
30  
31  
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },  
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },  
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },  
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },  
{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },  
{ },  
32  
33  
34  
35  
36  
37  
};  
38  
MODULE_DEVICE_TABLE(spi, m25p_ids);  
39  
40  
static const struct spi_device_id *jedec_probe(struct spi_device *spi)  
41  
{
42  
43  
}
44  
45  
static int m25p_probe(struct spi_device *spi)  
{
46  
47  
const struct spi_device_id *id = spi_get_device_id(spi);  
struct flash_platform_data *data;  
48  
49  
struct m25p  
*flash;  
*info;  
50  
struct flash_info  
unsigned  
51  
i;  
52  
struct mtd_part_parser_data ppdata;  
53  
struct device_node __maybe_unused *np = spi->dev.of_node;  
54  
55  
56  
57  
if (info->jedec_id) {  
58  
const struct spi_device_id *jid;  
59  
60  
jid = jedec_probe(spi);  
if (IS_ERR(jid)) {  
61  
62  
return PTR_ERR(jid);  
} else if (jid != id) {  
63  
64  
65  
66  
67  
68  
69  
70  
71  
dev_warn(&spi->dev, "found %s, expected %s\n",  
72  
jid->name, id->name);  
73  
id = jid;  
74  
info = (void *)jid->driver_data;  
75  
}
76  
}
77  
78  
79  
80  
if (data && data->name)  
flash->mtd.name = data->name;  
else  
81  
82  
83  
flash->mtd.name = dev_name(&spi->dev);  
84  
85  
flash->mtd.type = MTD_NORFLASH;  
86  
flash->mtd.writesize = 1;  
87  
flash->mtd.flags = MTD_CAP_NORFLASH;  
flash->mtd.size = info->sector_size * info->n_sectors;  
flash->mtd._erase = m25p80_erase;  
flash->mtd._read = m25p80_read;  
88  
89  
90  
91  
92  
93  
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {  
flash->mtd._lock = m25p80_lock;  
flash->mtd._unlock = m25p80_unlock;  
}
94  
95  
96  
97  
98  
99  
if (info->flags & SST_WRITE)  
flash->mtd._write = sst_write;  
else  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
flash->mtd._write = m25p80_write;  
if (info->flags & SECT_4K) {  
flash->erase_opcode = OPCODE_BE_4K;  
flash->mtd.erasesize = 4096;  
} else {  
flash->erase_opcode = OPCODE_SE;  
flash->mtd.erasesize = info->sector_size;  
}
}
static int m25p_remove(struct spi_device *spi)  
{
struct m25p *flash = dev_get_drvdata(&spi->dev);  
int  
status;  
status = mtd_device_unregister(&flash->mtd);  
if (status == 0) {  
kfree(flash->command);  
kfree(flash);  
}
return 0;  
}
static struct spi_driver m25p80_driver = {  
.driver = {  
.name = "m25p80",  
.owner = THIS_MODULE,  
},  
.id_table = m25p_ids,  
.probe = m25p_probe,  
.remove = m25p_remove,  
};  
module_spi_driver(m25p80_driver);  
MODULE_LICENSE("GPL");  
MODULE_AUTHOR("Mike Lavender");  
MODULE_DESCRIPTION("MTD SPI driver for ST M25Pxx flash chips");  
ꢀꢀ通读 m25p80.c 驱动代码,我们可以找出大概的脉络。首先是通过 module_spi_driver 函数注册 m25p80_driver 驱动,其中实现  
remove 函数,分别是 m25p_probe m25p_remove。并且填写了一张名为 m25p_ids 的兼容设备表,通过查看  
的数据手册,JEDEC 设备 ID 为 0xef4014,也就是对应 m25p_ids 中的 w25q80bl。  
1
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }  
2
ꢀꢀ其中 INFO 是一个宏定义,其作用是将设备参数填写到内部的 flash_info 结构体实例中,在设备匹配成功后使用。  
ꢀꢀ在 m25p_probe 函数中指定了 m25p80_readm25p80_write m25p80_erase 等文件操作函数,当应用程序使用 read、write、  
ioctl 等接口操作时最终会调用到这里。  
ꢀꢀ额,那 open 和 close 函数呢?  
ꢀꢀ还记得我们把 W25Q80DV 注册成 MTD 设备了嘛,所以另外一些操作函数在 drivers/mtd/mtdchar.c 中定义。实际上,它不仅有  
mtdchar_openmtdchar_close 等函数,还有 mtdchar_read mtdchar_write 函数,而它们会调用 m25p80.c 中的 m25p80_read 和  
m25p80_write 函数。  
static int mtdchar_open(struct inode *inode, struct file *file)  
1
2
{
int minor = iminor(inode);  
int devnum = minor >> 1;  
int ret = 0;  
3
4
5
struct mtd_info *mtd;  
struct mtd_file_info *mfi;  
struct inode *mtd_ino;  
6
7
8
9
// ...  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
}
static int mtdchar_close(struct inode *inode, struct file *file)  
{
// ...  
}
static ssize_t mtdchar_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)  
{
// ...  
}
static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)  
{
// ...  
}
设备树节点  
ꢀꢀ接下来要根据实际的硬件连接情况修改设备树文件,不用担心不会写,因为芯片厂商一定会提供参考信息的,比如这里的 SPI  
Flash,参考以下文件:  
ꢀꢀDocumentation/devicetree/bindings/mtd/m25p80.txt  
ꢀꢀ我这里的 W25Q80DV 连接到 i.MX6Q 的 ECSPI4,具体如下:  
&ecspi4 {  
fsl,spi-num-chipselects = <1>;  
1
2
cs-gpios = <&gpio3 20 0>;  
3
pinctrl-names = "default";  
4
pinctrl-0 = <&pinctrl_ecspi4_1 &pinctrl_ecspi4_cs_0>;  
status = "okay";  
5
6
7
flash: m25p80@0 {  
8
#address-cells = <1>;  
#size-cells = <1>;  
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  
compatible = "winbond,w25q80bl";  
spi-max-frequency = <20000000>;  
reg = <0>;  
};  
};  
&iomuxc {  
pinctrl-names = "default";  
pinctrl-0 = <&pinctrl_hog_1 &pinctrl_hog_2>;  
spi4 {  
pinctrl_ecspi4_cs_0: ecspi4_cs_grp-0 {  
fsl,pins = <  
MX6QDL_PAD_EIM_D20__GPIO3_IO20  
0x80000000  
0x80000000  
>;  
};  
pinctrl_ecspi4_cs_1: ecspi4_cs_grp-1 {  
fsl,pins = <  
MX6QDL_PAD_EIM_A25__GPIO5_IO02  
>;  
};  
pinctrl_ecspi4_1: ecspi4grp-1 {  
fsl,pins = <  
MX6QDL_PAD_EIM_D22__ECSPI4_MISO  
0x170f1  
0x1B008  
0x170f1  
MX6QDL_PAD_EIM_D28__ECSPI4_MOSI  
MX6QDL_PAD_EIM_D21__ECSPI4_SCLK  
>;  
};  
};  
};  
编译&验证  
ꢀꢀ重新编译 image 和 dtb:  
$ source /opt/poky/1.5.3/environment-setup-cortexa9hf-vfp-neon-poky-linux-gnueabi  
$ make uImage LOADADDR=0x10008000  
1
2
3
$ make imx6q-rom5420-b1.dtb  
ꢀꢀ更新系统后重新启动,进入 shell。输入命令 dmesg | grep spi,看到如下内容则说明内核已经探测到 w25q80bl 设备,把设备和  
驱动程序匹配上了。  
[ 1.931184] m25p80 spi32765.0: w25q80bl (1024 Kbytes)  
[ 1.935767] spi_imx 2014000.ecspi: probed  
1
2
ꢀꢀ查看设备文件:  
# ls /dev/mtd*  
/dev/mtd0  
1
2
3
/dev/mtd1  
/dev/mtdblock0  
/dev/mtdblock1  
/dev/mtd0ro  
/dev/mtd1ro  
ꢀꢀ可以看到多了 /dev/mtd1 之类的设备节点,其中 /dev/mtd1 是字符设备,/dev/mtdblock1 是块设备,/dev/mtd1ro 是只读字符  
设备。  
挂载 MTD 设备挂载  
ꢀꢀ因为我们把 SPI Flash 注册成 MTD 设备了,因此,我们可以通过 MTD 子系统和文件系统对其进行操作。  
ꢀꢀ首先对 Flash 进行格式化,然后挂载,接着就可以通过文件系统操作:  
# mkfs.vfat /dev/mtdblock1  
# mount -t vfat /dev/mtdblock1 /home/root/w25q80  
# cd /home/root/w25q80  
1
2
3
4
5
# echo "Hello W25Q80" > file.txt  
# sync  
ꢀꢀ然后断电重启,看看文件及其内容是否还在,并且与断电前一致。  
“读写擦”测试程序  
ꢀꢀ除了通过文件系统操作 W25Q80DV 设备外,也可以直接打开 /dev/mtd1 设备节点对其进行操作。  
博文
想对作者说点什么  
谢谢博主,MTD不知道为什么写不进去,读出来的都是 00 00.. (10个月前 #1楼)  
博文
博文
博文  
博文
博文
博文
博文
博文  
博文
博文
博文
博文
博文
博文
博文  
博文  
博文
博文
博文
博文  
博文
博文
博文
博文  
博文
博文
博文
博文
博文
博文
博文
博文
博文
博文
博文
博文
博文  
博文
博文
博文
博文

全部评论(0)

暂无评论