1 简介

    W25Q64是华邦公司推出的大容量SPI FLASH产品,其容量为8M。该25Q系列的器件在灵活性和性能方面远远超过普通的串行闪存器件。W25Q64将8M字节的容量分为128个块(block),每个块大小为64K字节,每个块又分为16个扇区(sector),每个扇区4K字节。W25Q64的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。所以,这需要给W25Q64开辟一个至少4K的缓存区,这样必须要求芯片有4K以上的SRAM才能有很好的操作。W25Q64的擦写周期多达10W次,可将数据保存达20年之久,支持2.7~3.6V的电压,支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可达80MHz。

    本文使用的硬件平台: Atmel SAMA5d4,在其平台上添加 W25Q64 芯片(SPI 设备),Linux 内核版本为 kernel-4.9.87,采用 DeviceTree 描述硬件连接信息。

2 硬件连接

  Atmel SAMA5d4是基于ARM Cortex-A5架构的高性能处理器,它上边有3组SPI接口(SPI0、SPI1、SPI2)。具体可以查看处理器芯片手册。

  处理器中的SPI引脚定义

  W25Q64与处理器的引脚定义及连接关系:

序号 W25Q64芯片引脚定义 处理器引脚定义
1 MOSI PC1
2 MISO PC0
3 SCLK PC2
4 CS PD31

3 驱动调试分析

3.1 理论基础

  先来看一些 flash的理论知识。

图 2.1 MTD设备的层次结构组成

  
    MTD层为NOR FLASH和NAND FLASH设备提供统一接口。MTD将文件系统与底层FLASH存储器进行了隔离。如图2.1所示,MTD设备通常可分为四层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。
    Flash硬件驱动层:(相当于spi driver/i2c driver),Flash硬件驱动层负责对Flash硬件的读、写和擦除操作。MTD设备的Nand Flash芯片的驱动则在drivers/mtd/nand/子目录下,Nor Flash芯片驱动位于drivers/mtd/chips/子目录下。
    MTD原始设备层:(相当于spi master/i2c client),用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数。其中mtdcore.c: MTD原始设备接口相关实现,mtdpart.c : MTD分区接口相关实现。
    MTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。其中mtdchar.c : MTD字符设备接口相关实现,mtdblock.c : MTD块设备接口相关实现。
    设备节点:通过mknod在/dev子目录下建立MTD块设备节点(主设备号为31)和MTD字符设备节点(主设备号为90)。通过访问此设备节点即可访问MTD字符设备和块设备。

3.2 设备树节点

    根据实际的硬件连接情况修改设备树文件。
    查看设备树文件(.dts 和 .dtsi)中spi0节点的属性;
    
.dtsi文件中:

引脚定义:

.dts文件中:
未修改前:

    从设备树spi0节点的子节点m25p80可知,内核自带的驱动程序中支持了 SPI FLASH芯片m25p80类型的驱动,原compatible属性对应的是型号为“at25df321a”的SPI串行闪存,进入源码文件m25p80.c(路径:\linux-at91\drivers\mtd\devices\m25p80.c),可以搜索到“at25df321a”在 m25p_ids结构体中,同时该结构体也包含了我们使用的SPI芯片w25q64,说明该驱动也可直接用于w25q64。

    重新配置设备树节点m25p80,我们这里使用的片选引脚为PD31,需要修改cs-gpios属性对应的值。另外还需修改m25p80节点中的compatible属性,修改为w25q64即可使能该flash。后续重新编译设备树文件生成.dtb,随内核一并烧录,就会成功加载w25q64驱动。
    修改后:

3.3 spi flash驱动流程分析

    这里要把SPI flash设备注册为MTD设备,MTD子系统实现了SPI flash芯片驱动程序,其驱动 Demo 为:

  • drivers/mtd/devices/mtd_dataflash.c
    (*Atmel AT45xxx DataFlash MTD driver for lightweight SPI framework)
  • drivers/mtd/devices/m25p80.c
    (MTD SPI driver for ST M25Pxx (and similar) serial flash chips)

    我们这里使用的是与m25p80相似的flash,所以套用源码文件m25p80.c,并没有对该文件进行修改。通读 m25p80.c 驱动代码,我们可以找出大概的脉络。首先是通过 module_spi_driver 函数注册 m25p80_driver 驱动,其中实现了 probe 和 remove 函数,分别是 m25p_probe 和 m25p_remove。并且填写了一张名为 m25p_ids 的兼容设备表,设备表中包含了"w25q64"。

    下面对驱动的调用过程进行详细分析。

    1) spi flash硬件驱动层部分
        SPI 协议驱动有些类似平台设备驱动:

    内核将调用 module_spi_driver() 这个宏来注册和卸载 spi 设备,这个 module_spi_driver 是专门针对于 spi 架构定义的。

    module_spi_driver

    继续追看 module_driver() 这个宏 ,它将 spi_register/unregister_driver() 与 module_init 和 module_exit 封装了起来。所以说实际上 module_spi_driver() 和 module_init/exit 几乎是没有区别的。之所以直接将其封装的原因是因为这个 SPI 设备本身是不可插拔的,也就不需要 init 和 exit 的过程,系统上电就直接注册了。

    module_driver -> module_init

spi_register_driver

__spi_register_driver

driver_register     * driver_register - register driver with bus

直接看 bus_add_driver

这里只截取一部分,最后调用的是driver_attach

在bus_for_each_dev执行了__driver_attach(dev, data),查看 __driver_attach

       在static int__driver_attach(struct device *dev, void *data)中先调用了driver_match_device(drv,dev),用于匹配,成功才继续执行,否则直接返回了。driver_match_device(drv, dev)中:

       如果match函数的指针不为空,则执行此bus的match函数,这也就是为什么老是说总线负责匹配设备和驱动了。这里也传递了参数struct device *dev。

       匹配成功继续执行,driver_attach(dev, data)中有个driver_probe_device(drv,dev),继续跟踪:

       有个really_probe(dev,drv),linux经常将一个函数传递给另一函数,后一个函数就是在前一个函数前加“do
”、“really
”、“__”,还经常宏定义或inline。
(截取部分代码)


       这里如果有总线上的probe函数就调用总线的probe函数,如果没有则调用drv的probe函数。

m25p_probe

/** board specific setup should have ensured the SPI clock used here* matches what the READ command supports, at least until this driver* understands FAST_READ (for clocks over 25 MHz).*/
static int m25p_probe(struct spi_device *spi)
{struct flash_platform_data  *data;struct m25p *flash;struct spi_nor *nor;struct spi_nor_hwcaps hwcaps = {.mask = (SNOR_HWCAPS_READ |SNOR_HWCAPS_READ_FAST |SNOR_HWCAPS_PP),};char *flash_name;int ret;data = dev_get_platdata(&spi->dev);flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);if (!flash)return -ENOMEM;nor = &flash->spi_nor;/* install the hooks */nor->read = m25p80_read;nor->write = m25p80_write;nor->write_reg = m25p80_write_reg;nor->read_reg = m25p80_read_reg;nor->dev = &spi->dev;spi_nor_set_flash_node(nor, spi->dev.of_node);nor->priv = flash;spi_set_drvdata(spi, flash);flash->spi = spi;if (spi->mode & SPI_RX_QUAD) {hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;if (spi->mode & SPI_TX_QUAD)hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 |SNOR_HWCAPS_PP_1_1_4 |SNOR_HWCAPS_PP_1_4_4);} else if (spi->mode & SPI_RX_DUAL) {hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;if (spi->mode & SPI_TX_DUAL)hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2;}if (data && data->name)nor->mtd.name = data->name;/* For some (historical?) reason many platforms provide two different* names in flash_platform_data: "name" and "type". Quite often name is* set to "m25p80" and then "type" provides a real chip name.* If that's the case, respect "type" and ignore a "name".*/if (data && data->type)flash_name = data->type;else if (!strcmp(spi->modalias, "spi-nor"))flash_name = NULL; /* auto-detect */elseflash_name = spi->modalias;ret = spi_nor_scan(nor, flash_name, &hwcaps);if (ret)return ret;return mtd_device_register(&nor->mtd, data ? data->parts : NULL,data ? data->nr_parts : 0);
}

       在 m25p_probe 函数中指定了 m25p80_read、m25p80_write 和m25p80_erase 等文件操作函数,当应用程序使用 read、write、ioctl 等接口操作时最终会调用到这里。那 open 和 close 函数呢? 我们把 W25Q64 注册成 MTD 设备了,所以另外一些操作函数在 drivers/mtd/mtdchar.c 中定义。实际上,它不仅有 mtdchar_open、mtdchar_close 等函数,还有 mtdchar_read 和 mtdchar_write 函数,而它们会调用 m25p80.c 中的 m25p80_read 和 m25p80_write 函数。



4 编译及开机验证

  
  (1)重新编译 image 和 dtb,更新系统后重新启动,进入系统。
输入命令 dmesg | grep spi,看到如下内容则说明内核已经探测到 w25q64 设备,把设备和驱动程序匹配上了。

  (2)查看设备文件:

    可以看到/dev/mtd1 之类的设备节点,其中 /dev/mtd1 是字符设备,/dev/mtdblock1 是块设备,/dev/mtd1ro 是只读字符设备。

  • cat /proc/mtd
  • cat /proc/partitions
  • mtd_debug info /dev/mtd7
  • mtdinfo /dev/mtd7

        从上面的命令返回结果可以看到,mtd7对应的设备即为Nor FLASH w25q64。

     (3)挂载 MTD 设备

    因为我们把 SPI Flash 注册成 MTD 设备了,因此,我们可以通过 MTD 子系统和文件系统对其进行操作。

    首先对 Flash 进行格式化,然后挂载,接着就可以通过文件系统操作:

    $ mkfs.vfat /dev/mtdblock7
    $ mount -t vfat /dev/mtdblock7 /home/root/w25q64
    $ cd /home/root/w25q64
    $ echo “Hello W25Q64” > file.txt
    $ sync

    然后断电重启,看看文件及其内容是否还在,并且与断电前一致。


mount命令:


    (4)“读写擦”测试

     除了通过文件系统操作 W25Q64 设备外,也可以直接打开 /dev/mtd1 设备节点对其进行操作。

     1)通过 mtd_debug 工具操作设备节点。

     2)通过C语言编写测试程序进行 “读写擦”操作。

    可参考该测试程序进行修改。
https://github.com/luhuadong/Linux-programming/blob/master/driver/mtd/test/mtd_go.c

Linux下SPI Flash-W25Q64驱动调试相关推荐

  1. linux下spi flash驱动程序,关于spi flash芯片m25p80驱动以及其简单的mtd驱动分析

    项目中用到了spi flash芯片MX25L25635E,之前在uboot下简单分析了驱动代码,调试该flash擦除的bug,一直没有时间分 析内核中关于该芯片的驱动,以下是对该芯片驱动的一个简单分析 ...

  2. linux下spi flash驱动程序,SPI Flash(W25Q16DV) 驱动

    大体上可分为以下几个部分: 1.注册设备驱动 spi_register_driver 2.分配 mtd_info 结构体 3.配置 mtd_info 结构体 4.注册 mtd_info 结构体 构建 ...

  3. linux 添加spi 驱动,Linux下SPI和IIC驱动免在设备树上添加设备信息的编写方法

    编写驱动时,一般需要往设备树上添加节点信息,这里提供一种直接在驱动中添加设备信息的方法. i2c的驱动模板如下 #include #include #define SENSOR_BUS_NUM 0 # ...

  4. nand flash 经典 全面 ------如何编写Linux下Nand Flash驱动

    Crifan Li 摘要 本文先解释了Nand Flash相关的一些名词,再从Flash硬件机制开始,介绍到Nand Flash的常见的物理特性,且深入介绍了Nand Flash的一些高级功能,然后开 ...

  5. 如何编写linux下nand flash驱动

    http://www.cnblogs.com/sankye/articles/1638852.html 向作者Sankye致敬 [编写驱动之前要了解的知识] 1.       硬件特性: [Flash ...

  6. *Linux下的USB总线驱动 u盘驱动分析*

    Linux下的USB总线驱动(三) u盘驱动分析 版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 https://www.xuebuyuan.com/13 ...

  7. 什么是 Linux 下的 platform 设备驱动

    Linux下的字符设备驱动一般都比较简单,只是对IO进行简单的读写操作.但是I2C.SPI.LCD.USB等外设的驱动就比较复杂了,需要考虑到驱动的可重用性,以避免内核中存在大量重复代码,为此人们提出 ...

  8. 最近在搞SPI Flash的驱动,有一个问题迟迟不能解决

    背景: 主芯片:MK60DN512ZVLQ10 / MK60DN512VLQ10 flash芯片:winbond 25Q64FV 明媚心(415741500)  11:10:44 请教一个spi FL ...

  9. Linux下的USB总线驱动 mouse

    Linux下的USB总线驱动(03)--USB鼠标驱动 usbmouse.c USB鼠标驱动 usbmouse.c 原文链接:http://www.linuxidc.com/Linux/2012-12 ...

  10. linux下使用VS CODE + CMAKE 调试C++程序

    Linux下使用VS Code + CMake 调试c++程序 - 灰信网(软件开发博客聚合)

最新文章

  1. pandas.get_dummies
  2. CRM_UI start port determination how is port number 44356 determined
  3. 传世的关系模型,巧夺天工的分布式数据库设计
  4. 使Eclipse下支持编写HTML/JS/CSS/JSP页面的自动提示。
  5. oracle安装包安装教程,oracle安装教程【搞定方案】
  6. 三点确定一个圆的计算方法
  7. 32位qt程序, 利用32位mysql驱动,连接64位mysql8.0
  8. kafka+fluentd+heka了解资料
  9. linux bt 命令行,在Linux终端下进行BT下载
  10. 基于上下采样的adaboost模型对信用卡欺诈数据进行识别
  11. DolphinScheduler 3.0.0-alpha 安装问题
  12. 【数字图像处理】双三次插值及其卷积算法(Bicubic Interpolation)
  13. JavaWeb实现打印
  14. COM组件 ATL的创建和调用
  15. 很好很强大的FXTZ
  16. apache进程数不断增多是什么原因造成的呢?
  17. php paerser,PHP: Sua primeira página PHP - Manual
  18. 汇付聚合支付自助接入解决方案快捷支付接口代码详解
  19. [Deeplearning4j应用教程00]_DL4J技术介绍
  20. quickfix的使用

热门文章

  1. 计算机基础——11种排序(sort)算法
  2. 云计算(期末复习题含答案)
  3. overflow解决float浮动后高度自适应问题
  4. 【渗透测试笔记】之【免杀工具——使用Invoke-Obfuscation代码混淆免杀powershell】
  5. jmeter接口测试
  6. SqlParameter防SQL注入的方法
  7. 员工工号怎么编码_员工编号管理制度
  8. java数组里的索引越界问题、空指针异常问题
  9. Kademlia、DHT、KRPC、BitTorrent 协议、DHT Sniffer
  10. Fedora 9 感受