嵌入式linux平台下的spi分为设备、总线和驱动,一般半导体原厂已经实现好了spi设备和总线的相关代码,开发者只需根据实际使用情况修改设备树以及编写驱动部分的代码即可。
迅为i.mx6ull开发板引出了一个spi4接口,其提供的设备树文件中spi4被用来驱动rc522和sx127x两个模块。
spi4 {
compatible = "spi-gpio";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi4>;
status = "okay";
gpio-sck = <&gpio5 11 0>;
gpio-mosi = <&gpio5 10 0>;
gpio-miso = <&gpio3 7 0>;
cs-gpios = <&gpio3 22 0>;
num-chipselects = <1>;
#address-cells = <1>;
#size-cells = <0>;
rc522: rc522@0 {
compatible = "rc522";
gpio-controller;
spi-tx-bus-width = <4>;
spi-rx-bus-width = <4>;
reset-rc522 = <&gpio3 5 0>;
reg = <0>;
spi-max-frequency = <4000000>;
};
};
为了快速上手嵌入式linux平台spi接口,我打算先编写一个简单的spi driver直接与官方提供的设备树中的rc522属性相匹配。这个简单驱动主要包括模块的出入口函数,以及spi设备的probe与remove函数,并在probe函数做一些初步的spi读写操作。
static int oled12864_driver_init(void)
{
int ret;
printk("oled12864_driver init!\n");
ret = spi_register_driver(&oled12864_driver);
if(ret < 0){
printk("spi_register_driver fail\n");
return ret;
}
return 0;
}
static void oled12864_driver_exit(void)
{
spi_unregister_driver(&oled12864_driver);
printk("oled12864_driver exit!");
}
module_init(oled12864_driver_init);
module_exit(oled12864_driver_exit);
MODULE_LICENSE("GPL");
int oled12864_probe(struct spi_device *spi)
{
int m,n;
printk("oled12864_driver matching spi deivce in the device-tree!\n");
spi->mode = SPI_MODE_0; /* 驱动匹配成功后,设置spi模式 */
spi_setup(spi);
oled_device = spi;
...
...
...
static struct spi_driver oled12864_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "oled12864_drv",
.of_match_table = oled12864_of_id,
},
.probe = oled12864_probe,
.remove = oled12864_remove,
.id_table = oled12864_id
};
按照事先设想,通过insmod装载该驱动后,在probe函数中打印一些信息以表示驱动和spi设备匹配成功。然而,实际上该驱动模块执行完入口函数后模块便没有了反应。
首先想到的是不是用引脚号冲突或者引脚复用冲突。检查了一变设备树发现spi4对应的gpio口并没有使用在其他地方,几个引脚复用也只在spi4这里用到了。
pinctrl_spi4: spi4grp {
fsl,pins = <
MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10 0x70a1 /*MOSI*/
MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11 0x70a1 /*SCK*/
MX6UL_PAD_LCD_DATA17__GPIO3_IO22 0x80000000 /*CS*/
MX6UL_PAD_LCD_DATA02__GPIO3_IO07 0x70a1 /*MISO*/
MX6UL_PAD_LCD_DATA00__GPIO3_IO05 0x17059 /* rc522 reset*/
>;
};
检查开发板的/sys/bus/spi/devices目录,也没有发现异常。于是,考虑是不是已经有驱动占用了spi4接口。进入linux源码根目录,通过make menuconfig配置内核源码,果不其然,在Device drivers->SPI support下找到了未被剔除的RC522 Module Driver support选项。
取消该驱动选项,并保存配置文件,保存好后会在内核目录生成一个.config文件。
迅为官方提供的编译脚本create.sh内容如下
!/bin/bash
export ARCH=arm
#export CROSS_COMPILE=/usr/local/arm/gcc-4.6.2-glibc-2.13-linaro-multilib-2011.12/fsl-linaro-toolchain/bin/arm-none-linux-gnueabi-
export CROSS_COMPILE=arm-linux-gnueabihf-
#make mrproper # means CLEAN
make topeet_defconfig
#if [ "$1" = "nand" ]
#then
# cp arch/arm/boot/dts/imx6ul-14x14-evk_nand.dts arch/arm/boot/dts/imx6ul-14x14-evk.dts
#else
# cp arch/arm/boot/dts/imx6ul-14x14-evk_emmc.dts arch/arm/boot/dts/imx6ul-14x14-evk.dts
#fi
make uImage LOADADDR=0x10008000 -j8
make modules
make topeet_emmc_4_3.dtb
make topeet_emmc_5_0.dtb
make topeet_emmc_7_0.dtb
make topeet_emmc_1024x600.dtb
make topeet_emmc_9_7.dtb
make topeet_emmc_10_1.dtb
make topeet_emmc_hdmi.dtb
make topeet_nand_4_3.dtb
make topeet_nand_5_0.dtb
make topeet_nand_7_0.dtb
make topeet_nand_1024x600.dtb
make topeet_nand_9_7.dtb
make topeet_nand_10_1.dtb
make topeet_nand_hdmi.dtb
cd ./arch/arm/boot/dts/
./create_dtb imx6ul_topeet_nand.dtb topeet_nand_4_3.dtb topeet_nand_7_0.dtb topeet_nand_10_1.dtb topeet_nand_1024x600.dtb topeet_nand_5_0.dtb topeet_nand_9_7.dtb topeet_nand_hdmi.dtb
其中make topeet_defconfig
命令就是以topeet_defconfig的配置编译内核源码,其文件路径为./arch/arm/configs/topeet_defconfig
。我们需要将更新后的配置文件.config作为新的topeet_defconfig文件。
cp -r .config arch/arm/configs/topeet_defconfig
然后用脚本./create.sh编译内核以及设备树,通过tftp启动更新后的linux内核,insmod装载spi驱动。可以看到,spi设备的probe函数成功执行。