一. 前言
上次通过GPIO模拟IIC的驱动一块0.98英寸的OLED后,想着下次用下GPIO模拟SPI接口试下。本文会介绍怎么用GPIO模拟SPI接口的方式为设备添加一块spi nor flash,flash模块如下图所示:
上图是一个spi nor模块,上面焊接了W25Q32FV(一块4M的spi nor flash),模块引出了flash的引脚,板子的GPIO接引脚来驱动flash。
板子采用的是一块MT7620A的开发板,内核的版本是Linux 3.18.45。
二. 步骤
1. CPU与模块的连线
模块 MT7620A板子
VCC 3.3V
GND GND
CS MDIO(GPIO#22)
DO SCL(GPIO#2)
CLK SDA(GPIO#1)
DI MDC(GPIO#23)
2. 内核修改
Device Drivers --->
[*] SPI support --->
<*> GPIO-based bitbanging SPI Master
内核SPI驱动选上"GPIO-based bitbanging SPI Master",表示内核支持GPIO模拟SPI接口。
3. 设备树修改
由于SPI用到了MDIO和I2C接口的GPIO,所以需要在pinctrl中将他们复用为普通GPIO模式,如下所示:
pinctrl {
state_default: pinctrl0 {
default {
ralink,group = "pa", "spi refclk", "wdt", "uartf", "nd_sd", "mdio", "i2c";
ralink,function = "gpio";
};
};
};
接下来添加spi模块相关的设备树,如下所示:
palmbus@10000000 {
gpio0: gpio@600 {
status = "okay";
};
gpio2: gpio@660 {
status = "okay";
};
gpio3: gpio@688 {
status = "okay";
};
spi@b00 {
status = "okay";
m25p80@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "mx25l12805d";
reg = <0 0>;
linux,modalias = "m25p80", "w25q128";
spi-max-frequency = <10000000>;
partition@0 {
label = "u-boot";
reg = <0x0 0x30000>;
read-only;
};
partition@30000 {
label = "u-boot-env";
reg = <0x30000 0x10000>;
read-only;
};
factory: partition@40000 {
label = "factory";
reg = <0x40000 0x10000>;
read-only;
};
partition@50000 {
label = "firmware";
reg = <0x50000 0xdb0000>;
};
partition@ff0000 {
label = "config";
reg = <0xe00000 0x100000>;
};
};
spi {
compatible = "spi-gpio";
#address-cells = <0x1>;
ranges;
gpio-sck = <&gpio0 1 0>;
gpio-miso = <&gpio0 2 0>;
gpio-mosi = <&gpio0 23 0>;
cs-gpios = <&gpio0 22 0>;
num-chipselects = <1>;
/* clients */
w25q32@1 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "w25q32";
reg = <0 0>;
spi-max-frequency = <10000000>;
partition@0 {
label = "other_flash";
reg = <0x0 0x0400000>;
};
};
};
};
从以上设备树可以看出,我们的设备原来也是使用了一块flash来存放uboot和内核等其他内容的。
compatible = "spi-gpio"会与spi-gpio驱动match,导致spi-gpio驱动probe函数运行,gpio-sck,gpio-miso,gpio-mosi和cs-gpios表示分别用什么GPIO模块spi对应的信号线,num-chipselects=<1>表示片选的个数为1。
w25q32@1表示spi的从设备了,这里就是我们的spi nor flash模块。"w25q32"表示我们使用的flash型号,这里指定的型号不对也没关系,内核会读flash的ID号,自动识别出flash的型号。
以上就是全部的修改了。
三. 效果
1. 修改之前的内核打印
[ 1.280000] m25p80 spi32766.0: found w25q256, expected mx25l12805d
[ 1.290000] m25p80 spi32766.0: w25q256 (32768 Kbytes)
[ 1.300000] 5 ofpart partitions found on MTD device spi32766.0
[ 1.320000] Creating 5 MTD partitions on "spi32766.0":
[ 1.330000] 0x000000000000-0x000000030000 : "u-boot"
[ 1.340000] 0x000000030000-0x000000040000 : "u-boot-env"
[ 1.350000] 0x000000040000-0x000000050000 : "factory"
[ 1.360000] 0x000000050000-0x000000e00000 : "firmware"
[ 1.370000] 0x000000e00000-0x000000f00000 : "config"
2. 修改之后的内核打印
[ 1.290000] m25p80 spi32766.0: w25q32 (4096 Kbytes)
[ 1.300000] 1 ofpart partitions found on MTD device spi32766.0
[ 1.310000] Creating 1 MTD partitions on "spi32766.0":
[ 1.320000] 0x000000000000-0x000000400000 : "other_flash"
[ 1.350000] m25p80 spi32765.0: found w25q256, expected mx25l12805d
[ 1.360000] m25p80 spi32765.0: w25q256 (32768 Kbytes)
[ 1.370000] 5 ofpart partitions found on MTD device spi32765.0
[ 1.380000] Creating 5 MTD partitions on "spi32765.0":
[ 1.390000] 0x000000000000-0x000000030000 : "u-boot"
[ 1.400000] 0x000000030000-0x000000040000 : "u-boot-env"
[ 1.410000] 0x000000040000-0x000000050000 : "factory"
[ 1.420000] 0x000000050000-0x000000e00000 : "firmware"
[ 1.430000] 0x000000e00000-0x000000f00000 : "config"
从以上的内核打印可以看出,内核识别到了一块w25q32 (4096 Kbytes)的flash,并且还创建了一块分区"other_flash",分区的大小是4M,这是我在设备上定义的。
3. 修改前的分区表
/ # cat /proc/mtd
dev: size erasesize name
mtd0: 00030000 00010000 "u-boot"
mtd1: 00010000 00010000 "u-boot-env"
mtd2: 00010000 00010000 "factory"
mtd3: 00db0000 00010000 "firmware"
mtd4: 00100000 00010000 "config"
4. 修改后的分区表
/ # cat /proc/mtd
dev: size erasesize name
mtd0: 00400000 00010000 "other_flash"
mtd1: 00030000 00010000 "u-boot"
mtd2: 00010000 00010000 "u-boot-env"
mtd3: 00010000 00010000 "factory"
mtd4: 00db0000 00010000 "firmware"
mtd5: 00100000 00010000 "config"
5. mount "other_flash"分区
/ # mount -t jffs2 /dev/mtdblock0 /media
[ 560.280000] jffs2: notice: (2013) jffs2_build_xattr_subsystem: complete building xattr subsystem, 0 of xdatum (0 unchecked, 0 orphan) and 0 of xref (0 dead, 0 orphan) found.
/ #
/ # df -h
Filesystem Size Used Available Use% Mounted on
tmpfs 61.2M 0 61.2M 0% /tmp
/dev/mtdblock0 4.0M 260.0K 3.7M 6% /media
从以上打印可以看出,新的4M的spi nor flash就成功mount到了/media目录。
四. 总结
本文介绍了Linux通过GPIO模块SPI接口的方式,并且通过实例介绍了怎么驱动一块spi nor flash。这里有一处缺点:就是"other_flash"分区总是mtd0,这样会导致分区号整体向后偏移1,导致原来的代码需要修改这里还没有找到有效的办法解决。