1、前言
最近使用preempt rt linux内核,没有spi驱动。之前写的spi驱动主要是通过修改 arch/arm/mach-omap2/board-am335xevm.c文件实现的,但是这个rt内核没有board-am335xevm.c文件。研究了好久,才发现它是利用设备树来配置或挂载驱动的。
本人对驱动还不甚了解,将探索过程记录如下,错误之处请各位大牛斧正!
2、设备树文件dtb和dtsi
板子的设备树文件后缀一般为dtb,其通用部分的一些设备可用dtsi文件描述,即dtb包含dtsi。
dtb和dtsi文件一般位于内核的arch/arm/boot/dts目录下。下图是我用的开发板的dts文件夹内容,主要修改的是板子对应的tq335x-coreb-v2-can.dtb,以及芯片设备树文件am33xx.dtsi。
3、添加spi设备
am33xx默认已经包含了spi0和spi1节点,如果只生成spidev0.0或spidev1.0,此步骤可以跳过。
如果需要生成spidev2.0驱动,需要在am33xx.dtsi再添加1个spi设备。
打开am33xx.dtsi,在aliases {中添加:
spi2 = &spi2;
如图:
因为实际使用的是spi1引脚,但又要生成spidev2.0,所以要将spi的地址修改为没有用到的其它设备地址(最好不要用自定义的地址,可能会导致kernel不能启动)。因为我板子上的i2c2没有用(dts文件中没有配置),所以将它的配置给spi0好了。
/*@后的地址是i2c2的地址*/
spi1: spi@4819c000 {
compatible = "ti,omap4-mcspi";
#address-cells = <1>;
#size-cells = <0>;
ti,hwmods = "i2c3";
reg = <0x4819c000 0x1000>;
interrupts = <30>;
status = "disabled";
};
然后将原来的spi1改为spi2:
spi2: spi@481a0000 {
compatible = "ti,omap4-mcspi";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x481a0000 0x400>;
interrupts = <125>;
ti,spi-num-cs = <2>;
ti,hwmods = "spi1";
dmas = <&edma 42 0
&edma 43 0
&edma 44 0
&edma 45 0>;
dma-names = "tx0", "rx0", "tx1", "rx1";
status = "disabled";
};
4、修改dts文件
4.1 配置spi1引脚
因为实际用到spi1,所以要配置spi1引脚。
在tq335x-coreb-v2-can.dtb的spi0_pins: pinmux_spi0_pins {……}后面添加:
spi1_pins: pinmux_spi1_pins {
pinctrl-single,pins = <
0x190 (PIN_INPUT_PULLUP | MUX_MODE3) /* spi1-sclk gpio3_14*/
0x194 (PIN_INPUT_PULLUP | MUX_MODE3) /* spi1-do gpio3_15*/
0x198 (PIN_INPUT_PULLUP | MUX_MODE3) /* spi1-di gpio3_16*/
0x19c (PIN_INPUT_PULLUP | MUX_MODE3) /* spi1-cs0 gpio3_17*/
>;
};
4.2 挂载spidev0.0驱动
修改&spi0{}的内容,如下所示,重新编译内核后,应该可以看到spidev0.0驱动了。
&spi0 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins>;
ti,pindir-d0-out-d1-in;
spidev@0 {
compatible = "rohm,dh2228fv";
spi-max-frequency = <48000000>;
reg = <0>;
};
};
4.3 挂载spidev1.0驱动
同spi0一样,修改&spi1{}的内容,如下所示,重新编译内核后,应该可以看到spidev0.0驱动了。
&spi1 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi1_pins>;
ti,pindir-d0-out-d1-in;
spidev@0 {
compatible = "rohm,dh2228fv";
spi-max-frequency = <48000000>;
reg = <0>;
};
};
注:要使用spidev1.0,应跳过第3步骤,不用修改am33xx.dtsi文件。
4.4 挂载spidev2.0
同spi0一样,增加一个&spi2{},如下所示,重新编译内核后,应该可以看到spidev2.0驱动了。
&spi2 {
status = "okay"; /*使能设备驱动*/
pinctrl-names = "default";
pinctrl-0 = <&spi1_pins>;
ti,pindir-d0-out-d1-in;
spidev@0 {
/*compatible要与driver/spi/spidev.c的匹配一致*/
compatible = "rohm,dh2228fv";
spi-max-frequency = <48000000>; /*最大频率*/
reg = <0>; /*片选信号*/
};
};
注:spi2使用的是spi1_pins。
5、spidev驱动配置小结
这里仅为个人经验总结,错误之处,望留言指出。
1)spi驱动一般显示为:spidevX.Y,X是spi节点数量,Y是子节点数量?(尝试配置了2个以上的节点,只能显示spidevX.0和spidevX.1);
2)spi节点数量在am33xx.dtsi中配置,地址(@后的数值)不能与其它spi节点一样,但可以与其它非spi的节点一样。最好使用其它不用节点的配置,不要自己随便写。
3)dtb文件中配置spi的子节点,关键点有2个:
①status必须设置为“okay”,表示使能。若为disable,则不会挂载;
②compatible要与driver/spi/spidev.c中static const struct of_device_id spidev_dt_ids[]的compatible的其中一个的完全一致,否则不能挂载设备。其内容如下:
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "rohm,dh2228fv" },
{ .compatible = "lineartechnology,ltc2488" },
{ .compatible = "ge,achc" },
{ .compatible = "semtech,sx1301" },
{},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
4)compatible匹配后,在spidev.c中会对驱动命名:
static struct spi_driver spidev_spi_driver = {
.driver = {
.name = "spidev",
……
5)在spidev.c的static int spidev_probe(struct spi_device *spi)函数中,会根据spi设备节点数显示相应数值:
if (minor < N_SPI_MINORS) {
struct device *dev;
spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
dev = device_create(spidev_class, &spi->dev, spidev->devt,
spidev, "spidev%d.%d", //显示spidevX.Y
spi->master->bus_num, spi->chip_select);
status = PTR_ERR_OR_ZERO(dev);
} else {
dev_dbg(&spi->dev, "no minor number available!\n");
status = -ENODEV;
}