Linux SPI W25Q128驱动
前一段时间在给一块板子移植驱动,在SPI驱动部分踩到好大一个坑,记录分享一下
板子的主控芯片用的是I.MX6ULL,板子上有一颗华邦W25Q128的FLASH芯片,通过SPI总线与主控连接,所以需要在设备树里添加SPI设备节点。起初我参照正点原子驱动开发指南的Linux SPI驱动部分给的例子写了一个设备节点,如下所示
&ecspi3 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio4 12 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
flash: m25p80@0 {
compatible = "winbond,w25q128";
spi-max-frequency = <2000000>;
reg = <0>;
};
};
pinctrl_ecspi3: w25q128 {
fsl,pins = <
MX6UL_PAD_NAND_CE0_B__GPIO4_IO13 0x10b1
MX6UL_PAD_NAND_CE1_B__GPIO4_IO14 0x10b1
MX6UL_PAD_NAND_CLE__GPIO4_IO15 0x10b1
MX6UL_PAD_NAND_READY_B__GPIO4_IO12 0x10b0
>;
};
编译设备树烧录之后,FLASH芯片并不能被注册,内核启动信息报错m25p80 spi32766.0: unrecognized JEDEC id bytes: ff, ff, ff
,ID信息全为0xff,证明SPI读取过程出错了。用示波器看了一下波形发现一个很奇怪的现象,就是SPI每发送一个字节,片选信号就会拉高一次,如下图所示(图中蓝色信号为CS,黄色信号为MOSI)
但是之前在用STM32驱动华邦的W25Q128芯片时,片选信号只有在一帧的起始和结束的时候才会变化,我又查了查W25Q128的芯片资料,发现确实如此。下图为主机读取W25Q128芯片的流程图,可以看到片选信号只在起始时拉低,结束时拉高。
经过苦苦的查找,终于让我发现了问题的原因。参照《IMX6ULL参考手册》如下图所示章节,原来I.MX6ULL芯片的ECSPI
作为主机端发送数据时,是有单连发和多连发的区别的,寄存器ECSPIx_CONFIGREG[SS_CTL]
就用来控制模式的选择。当SS_CTL[3:0] = 0
时为单连发模式,片选信号在准备发送数据时拉低(选中),结束发送时拉高,在发送字节数据的间隔是不会变化的。当SS_CTL[3:0] = 1
时为多连发模式,每发送完一个字节,片选信号就会拉高拉低一次。
虽然知道了问题出在SS_CTL标志位上,但是撸了两天SPI的驱动源码也没能将这个标志位置0,所以只能另寻出路,然后就想能不能用GPIO来模拟SPI接口。
答案是可以的,参照spi-gpio的绑定文档http://devicetree.org/schemas/spi/spi-gpio.yaml
,写了新的设备树
spi4 {
compatible = "spi-gpio";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi4>;
status = "okay";
cs-gpios = <&gpio4 12 0>;
gpio-sck = <&gpio4 13 0>;
gpio-mosi = <&gpio4 14 0>;
gpio-miso = <&gpio4 15 0>;
num-chipselects = <1>;
#address-cells = <1>;
#size-cells = <0>;
flash: m25p80@0 {
compatible = "winbond,w25q128";
#address-cells = <1>;
#size-cells = <1>;
spi-max-frequency = <2000000>;
reg = <0>;
};
};
pinctrl_spi4: spi4grp {
fsl,pins = <
MX6UL_PAD_NAND_CE0_B__GPIO4_IO13 0x70a1
MX6UL_PAD_NAND_CE1_B__GPIO4_IO14 0x70a1
MX6UL_PAD_NAND_CLE__GPIO4_IO15 0x10b0
MX6UL_PAD_NAND_READY_B__GPIO4_IO12 0x10b0
>;
};
同时,也要在menuconfig
中将GPIO-based bitbanging SPI Master
这一项勾选上,编译出spi-gpio的驱动
location:
-> Device Drivers │
-> SPI support (SPI [=y])
->GPIO-based bitbanging SPI Master
再次查看开机启动信息,发现flash芯片的ID信息已经被成功识别
root@imx6ull-p557-01:/# dmesg | grep spi
[ 1.295136] m25p80 spi32766.0: w25q128 (16384 Kbytes)
FLASH芯片已经被成功注册了,那怎么在应用层读写FLASH芯片呢?
这就涉及到MTD子系统,因为我们用的驱动是m25p80.c这个文件(设备节点中的"winbond,w25q128"
即为m25p80.c中的属性),这个驱动会将FLASH芯片被注册成MTD设备,MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,可以说MTD是针对Flash设备设计的标准化硬件驱动框架。
FLASH芯片已经被注册成MTD设备,文件系统中 /dev 目录下mtd相关的3个文件即为flash注册的mtd设备,mtd0是对应的字符设备,mtdblock0是对应的块设备,mtd0ro是只读字符设备
root@localhost:/# ls /dev/mtd*
/dev/mtd0 /dev/mtd0ro /dev/mtdblock0
因为我们把 SPI Flash 注册成 MTD 设备了,因此,我们可以通过 MTD 子系统和文件系统对其进行操作
首先对 Flash 进行格式化,然后挂载,接着就可以通过文件系统操作:
$ mkfs.vfat /dev/mtdblock0
$ mount -t vfat /dev/mtdblock0 /media/w25q128
$ cd /media/w25q128
$ echo “Hello W25Q128” > file.txt
$ syn
然后断电重启,用mount命令重新挂在后查看file.txt文件是否还在,并查看文件内容
参考博客:
Linux下SPI Flash-W25Q64驱动调试_heat.huang的博客-CSDN博客_zynq w25q 设备树
SPI 两帧数据之间的间隔-云社区-华为云 (huaweicloud.com)
【正点原子】I.MX6U嵌入式Linux驱动开发指南