SPI调试指南
驱动代码
代码路径
drivers/spi/spidev.c # 生成字符设备节点,可供用户空间操作
drivers/spi/spi.c # spi框架层代码
drivers/spi/spi-hobot.c # spi驱动层代码
内核配置
CONFIG_SPI_SPIDEV=y # spidev.c配置选项
CONFIG_SPI_SLAVE=y # CONFIG_SPI_SPIDEV依赖的配置选项
CONFIG_SPI_HOBOT=y # spi-hobot.c驱动层配置选项
DTS设备节点配置
在下述文件中添加相应的设备节点 ,并对内核进行编译。 文件路径为:arch/arm64/boot/dts/hobot/hobot-xj3.dtsi
spi0: spi@0xA5004000 {
compatible = "hobot,hobot-spi";
reg = <0 0xA5004000 0 0x1000>;
clocks = <&spi0_mclk>;
clock-names = "spi_mclk";
interrupt-parent = <&gic>;
interrupts = <0 33 4>;
resets = <&rst 0x50 4>;
reset-names = "spi0";
pinctrl-names = "default";
pinctrl-0 = <&spi0_func>;
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
};
文件路径:arch/arm64/boot/dts/hobot/hobot-x3-sdb.dts
/* 配置为master */
&spi0 {
status = "okay";
spidev@0x00 {
compatible = "rohm,dh2228fv";
spi-max-frequency = <20000000>;
reg = <0>;
};
};
/* 配置为slave */
&spi2 {
status = "okay";
slave = <1>;
slave@0x00 {
compatible = "rohm,dh2228fv";
spi-max-frequency = <20000000>;
reg = <0>;
};
};
以spi0和spi2配置为例
- hobot-xj3.dtsi中的节点为公用的节点基本不用修改,针对不同的硬件,会在对应的dts中进行修改
- spi0配置为spi master,spi2配置为spi slave,其中spi2中 isslave = <1> 属性表示该spi配置为slave
- 两个节点中的spidev@0x00、slave@0x00节点会在spidev.c中被识别成为/dev/spidev0.0和/dev/spidev2.0设备节点,供用户空间操作
SPI驱动
驱动位于:drivers/spi/spi-hobot.c
SPI master/slave配置
static int hb_spi_probe(struct platform_device *pdev)
{
...
/* master or slave mode select */
isslave = of_property_read_bool(pdev->dev.of_node, "slave");
if (isslave == MASTER_MODE) {
ctlr = spi_alloc_master(&pdev->dev, sizeof(*hbspi));
if (!ctlr) {
dev_err(&pdev->dev, "failed to alloc spi master\n");
return -ENOMEM;
}
} else if (isslave == SLAVE_MODE) {
ctlr = spi_alloc_slave(&pdev->dev, sizeof(*hbspi));
if (!ctlr) {
dev_err(&pdev->dev, "failed to alloc spi slave, try master\n");
return -ENOMEM;
}
}
...
}
SPI注册
向内核注册SPI控制器
static int hb_spi_probe(struct platform_device *pdev)
{
...
if (isslave == MASTER_MODE) {
hbspi->isslave = MASTER_MODE;
snprintf(ctrl_mode, sizeof(ctrl_mode), "%s", "master");
ctlr->bus_num = pdev->id;
// ctlr->num_chipselect = HB_SPI_MAX_CS;
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH |
SPI_NO_CS;
ctlr->setup = hb_spi_setup;
ctlr->prepare_transfer_hardware = hb_spi_prepare_xfer_hardware;
ctlr->transfer_one = hb_spi_transfer_one;
ctlr->unprepare_transfer_hardware = hb_spi_unprepare_xfer_hardware;
ctlr->set_cs = hb_spi_chipselect;
ctlr->dev.of_node = pdev->dev.of_node;
} else if (isslave == SLAVE_MODE) {
hbspi->isslave = SLAVE_MODE;
snprintf(ctrl_mode, sizeof(ctrl_mode), "%s", "slave");
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
ctlr->setup = hb_spi_slave_setup;
ctlr->prepare_message = hb_spi_slave_prepare_message;
ctlr->transfer_one = hb_spi_slave_transfer_one;
ctlr->slave_abort = hb_spi_slave_abort;
}
/* register spi controller */
ret = devm_spi_register_controller(&pdev->dev, ctlr);
if (ret) {
dev_err(&pdev->dev, "failed to register %s controller(%d)\n",
ctrl_mode, ret);
goto clk_dis_mclk;
}
...
}
硬件初始化
硬件初始化函数如下,寄存器含义可通过与D-Robotics 相关确认
/* spi hw init */
static void hb_spi_init_hw(struct hb_spi *hbspi)
{
u32 val = 0;
/* First, should reset the whole controller */
hb_spi_reset(hbspi);
hb_spi_en_ctrl(hbspi, HB_SPI_OP_CORE_DIS, HB_SPI_OP_NONE,
HB_SPI_OP_NONE);
hb_spi_wr(hbspi, HB_SPI_INTSETMASK_REG, HB_SPI_INT_ALL);
/* clear all interrupt pending */
hb_spi_wr(hbspi, HB_SPI_SRCPND_REG, HB_SPI_INT_ALL);
/* init rfto */
hb_spi_wr(hbspi, HB_SPI_RFTO_REG, 0x27F);
/* no instruction */
hb_spi_wr(hbspi, HB_SPI_INST_REG, 0x0);
hb_spi_wr(hbspi, HB_SPI_INST_MASK_REG, 0xFFFFFFFF);
/* spi master mode */
val = hb_spi_rd(hbspi, HB_SPI_CTRL_REG);
if (hbspi->isslave == SLAVE_MODE)
val |= HB_SPI_SLAVE_MODE;
else
val &= (~HB_SPI_SLAVE_MODE);
if (hbspi->isslave == MASTER_MODE)
val &= (~HB_SPI_SAMP_SEL);
hb_spi_wr(hbspi, HB_SPI_CTRL_REG, val);
if (debug)
dev_err(hbspi->dev, "%s CTRL=%08X\n",
__func__, hb_spi_rd(hbspi, HB_SPI_CTRL_REG));
hb_spi_config(hbspi);
hb_spi_en_ctrl(hbspi, HB_SPI_OP_CORE_EN, 0, 0);
}
调试参数
下列为spi驱动中输出的调试参数:
static int debug;
static int slave_tout = 2000;
static int master_tout = 1000;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "spi: 0 close debug, other open debug");
module_param(slave_tout, int, 0644);
MODULE_PARM_DESC(slave_tout, "spi: slave timeout(sec), default 10 s");
module_param(master_tout, int, 0644);
MODULE_PARM_DESC(master_tout, "spi: master timeout(sec), default 2 s");
- debug等级可以设置为0,1,2,默认值为0
- slave超时时间默认是2s,有效最大值是100s
- master超时时间默认是1s,有效最大值是10s
在内核命令行内使用sysfs的修改方法如下,sysfs内单位均为毫秒。 找到可用参数:如下,包含3个参数
ls /sys/module/spi_hobot/parameters/
以下打印应出现
root@x3dvbj3-hynix2G-2666:~# ls /sys/module/spi_hobot/parameters/
debug master_tout slave_tout
获取当前debug参数的值:默认值为0,即不开启debug
cat /sys/module/spi_hobot/parameters/debug
以下打印应出现
root@x3dvbj3-hynix2G-2666:~# cat /sys/module/spi_hobot/parameters/debug
0
设置debug参数值为1,并再次确认设置成功:
echo 1 > /sys/module/spi_hobot/parameters/debug
cat /sys/module/spi_hobot/parameters/debug
以下打印应出现:
root@x3dvbj3-hynix2G-2666:~# echo 1 > /sys/module/spi_hobot/parameters/debug
root@x3dvbj3-hynix2G-2666:~# cat /sys/module/spi_hobot/parameters/debug
1
获取当前master_tout参数,即作为master超时时间的值:默认值为2s
cat /sys/module/spi_hobot/parameters/master_tout
以下打印应出现:
root@x3dvbj3-hynix2G-2666:~# cat /sys/module/spi_hobot/parameters/master_tout
1000
获取当前slave_tout参数,即作为slave超时时间的值:默认值为1s
cat /sys/module/spi_hobot/parameters/slave_tout
以下打印应出现:
root@x3dvbj3-hynix2G-2666:~# cat /sys/module/spi_hobot/parameters/slave_tout
2000
SPI测试
硬件回环测试
内核dts使能spi0为master模式:
/* 配置为master */
&spi0 {
status = "okay";
spidev@0x00 {
compatible = "rohm,dh2228fv";
spi-max-frequency = <20000000>;
reg = <0>;
};
};
可以观察到spidev0.0设备节点
# ls /dev/spidev0.0
/dev/spidev0.0
使用连接 器把spi的MOSI 和 MISO 两个管脚连接起来
测试代码
编译spidev_tc.c 代码,具体代码如附录A
/opt/gcc-ubuntu-9.3.0-2020.03-x86_64-aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -o spidev_tc spidev_tc.c -lpthread
回环测试命令:打开/dev/spidev0.0,设置12MHz速率,读写同时进行,每次读写1000个字节,测试50轮
# ./spidev_tc -D /dev/spidev0.0 -s 12000000 -m 3 -e 1000 -t 50
spi mode: 0x0
bits per word: 8
max speed: 12000000 Hz (12000 KHz)
userspace spi read and write test, len=1000 times=50
test: OK, times=0
test: OK, times=1
...
test: OK, times=49
spidev_tc 命令是测试spi的工具集命令,可以阅读它的帮助信息获取更多使用方法。