背景
RK3588平台需要使用spi协议与自主设计的加密芯片进行交互,加密芯片使用spidev驱动进行交互,该芯片要求在每次进行传输前拉低片选信号线(CS)50ms以上,否则会报告通讯失败。
问题
使用上述spidev驱动代码可以与加密芯片正常交互,但是芯片的返回结果是错误的,使用示波器观察信号质量以及信号时序未发现错误,排查由于硬件设计导致的时序错误问题。与芯片的fae交流得知在每次交互前需要将片选信号(cs)拉低50ms以上,但是user mode spi driver并未提供相应的接口配置该延时时间。
解决方法
从瑞芯微提供的驱动底层入手,观察每次的spi传输过程。
RK3588s.dtsi中设备树中spi控制器的定义如下:
spi0: spi@feb00000 {
compatible = "rockchip,rk3066-spi";
reg = <0x0 0xfeb00000 0x0 0x1000>;
interrupts = <GIC_SPI 326 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&cru CLK_SPI0>, <&cru PCLK_SPI0>;
clock-names = "spiclk", "apb_pclk";
dmas = <&dmac0 14>, <&dmac0 15>;
dma-names = "tx", "rx";
pinctrl-names = "default";
pinctrl-0 = <&spi0m0_cs0 &spi0m0_cs1 &spi0m0_pins>;
num-cs = <2>;
status = "disabled";
};
根据compatible属性搜索使用的驱动,搜索结果如下:
./spi-rockchip.c: { .compatible = "rockchip,rk3066-spi", },
在该驱动的probe函数中调用devm_spi_register_controller向spi子系统注册spi控制器,进一步追踪devm_spi_register_controller,该函数调用spi_register_controller,该函数进一步调用spi_controller_initialize_queue,spi_controller_initialize_queue初始化spi队列化传输,spi传输由一个kernel thread负责,spi传输队列的pump操作最终由spi_pump_message实现,该函数调用ctlr->transfer_one_message进行一次数据传输,而ctlr->transfer_one_message在spi_controller_initialize_queue中被初始化为spi_transfer_one_message。也就是每一次的spi传输最终由spi_transfer_one_message实现。在spi_transfer_one_message中首先调用spi_set_cs将cs拉低,然后调用平台特定的ctlr->transfer_one完成一次数据传输。因此我们只需要在spi_set_cs后调用udelay(50)即可完成目标。梳理一下代码
//spi-rockchip.c
rockchip_spi_probe //rk3588平台spi驱动的探测函数调用devm_spi_register_controller
//spi.c
devm_spi_register_controller //对spi_register_controller的封装
//spi.c
spi_register_controller //向spi core注册spi控制器 调用 spi_controller_initialize_queue
// spi.c
spi_controller_initialize_queue
// 初始化ctlr->transfer_one_message = spi_transfer_one_message; 调用spi_init_queue 初始化spi队列化传输,spi队列化传输由__spi_pump_messages实现 __spi_pump_messages调用ctlr->transfer_one_message
//spi.c
spi_transfer_one_message //调用spi_set_cs拉低cs线,调用ctrl->transfer_one完成一次传输