【问题标题】:How to fix an SPI driver that always fails validation in the Linux kernel SPI driver如何修复在 Linux 内核 SPI 驱动程序中始终无法通过验证的 SPI 驱动程序
【发布时间】:2019-09-08 04:10:35
【问题描述】:

我正在运行使用 Yocto (Pyro) 构建的嵌入式 Linux (4.14.16)。我在具有 i.MX6DL 且 SPI 连接到 FPGA(Xilinx Artix 7)的定制板上运行。我目前正在编写一个属于抽象层的类,因此该代码位于 Linux 驱动程序之上。它不是 Linux 设备驱动程序。 SPI 工作;我可以使用 shell 脚本对 FPGA 进行编程,如果将数据回显到 /dev/spi1.0 中,则可以查看 SPI 流量(FPGA 将 SPI 输出到标头,我连接了一个分析器)。

问题是当我使用我的驱动程序尝试读取 FPGA 中的寄存器时,它没有发送任何内容; SPI传输不会发生。

我稍微研究了 Linux 中的 spidev 和 spi 驱动程序,我发现它在驱动程序/spi/spi.c 中的 /* check transfer rx_nbits */ 注释下的 __spi_validate 调用失败。是什么控制着这些位?我们板上的所有内容都是每个时钟的单个数据位,我们没有 quad-spi。

这里是有问题的代码:

#include "os/drivers/buses/linuxos/spi_driver.h"

#include <fcntl.h>
#include <sstream>
#include <stdio.h>
#include <unistd.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>

namespace os
{
namespace drivers
{
namespace buses
{
namespace linuxos
{
spi_driver::spi_driver(int bus_id, int cs_index, std::uint32_t speed_bps) :
    m_speed_bps(speed_bps)
{
    std::stringstream descriptor;
    descriptor << "/dev/spidev" << bus_id << '.' << cs_index;
    m_device_file_descriptor = descriptor.str();
}

bool spi_driver::transfer(const unsigned char *out_data, unsigned char *in_data, size_t size_in_bytes)
{
    int spi_file_handle = open(m_device_file_descriptor.c_str(), O_RDWR);
    bool success = (spi_file_handle >= 0);

    if (success)
    {
        printf("spidev opened\n");
        struct spi_ioc_transfer transfer_parameters;
        transfer_parameters.tx_buf = reinterpret_cast<unsigned long>(out_data);
        transfer_parameters.rx_buf = reinterpret_cast<unsigned long>(in_data);
        transfer_parameters.len = size_in_bytes;
        transfer_parameters.speed_hz = m_speed_bps;
        transfer_parameters.bits_per_word = 0;
        // transfer_parameters.cs_change = 0;
        // transfer_parameters.delay_usecs = 0;

        int ioctl_return = ioctl(spi_file_handle, SPI_IOC_MESSAGE(1), &transfer_parameters);

        printf("spidev ioctl returned %d\n", ioctl_return);

        success = (ioctl_return > 0);

        printf("Received data: ");
        for (unsigned int i = 0; i < size_in_bytes; i++)
        {
            printf("%02x ", in_data[i]);
        }
        printf("\n");
    }

    close(spi_file_handle);

    return success;
}
}
}
}
}

我不确定它是否相关(SPI 似乎可以工作...),但这是设备树的 SPI 部分。

&ecspi2 {
    cs-gpios = <&gpio5 12 GPIO_ACTIVE_HIGH>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_ecspi2 &pinctrl_ecspi2_cs>;
    status = "okay";

    simulator-fpga {
        compatible = "mi,simulator-fpga";
        spi-max-frequency = <8000000>;
        reg = <0>;
    };
};

mi,simulator-fpga 已添加到通用 spidev 驱动程序的兼容字符串中,因为如果您直接使用 spidev,它会打印错误,但它是通用 spidev 设备。

我确实尝试过只使用 Linux 读写功能,它有效(我在 SPI 分析器上看到了流量),但我需要全双工传输,而这无法通过该方法完成。

编辑:如果有人想知道那些 printf 语句打印出来的内容,这是我从中得到的:

spidev opened
spidev ioctl returned -1
Received data: 00 00 00 00 00 00 00 00

接收到的数据是发送所需消息的正确长度。我不确定为什么返回值是 -1,内核中 spi.c 中抛出的错误是 -22(EINVAL/无效参数),如上所述。

【问题讨论】:

    标签: c++ linux embedded embedded-linux spi


    【解决方案1】:

    所以我不确定为什么需要这样做,但这个版本的驱动程序似乎可以工作。我添加了this example. 中的所有额外 ioctl 虽然我的 FPGA 仍然不喜欢输出(即使它在 3.14 上运行良好),但它在逻辑分析仪和协议分析仪上看起来是正确的。

    #include "os/drivers/buses/linuxos/spi_driver.h"
    
    #include <fcntl.h>
    #include <sstream>
    #include <stdio.h>
    #include <unistd.h>
    #include <linux/spi/spidev.h>
    #include <sys/ioctl.h>
    
    namespace os
    {
    namespace drivers
    {
    namespace buses
    {
    namespace linuxos
    {
    spi_driver::spi_driver(int bus_id, int cs_index, std::uint32_t speed_bps) :
        m_speed_bps(speed_bps)
    {
        std::stringstream descriptor;
        descriptor << "/dev/spidev" << bus_id << '.' << cs_index;
        m_device_file_descriptor = descriptor.str();
    }
    
    bool spi_driver::transfer(const unsigned char *out_data, unsigned char *in_data, size_t size_in_bytes)
    {
        int spi_file_handle = open(m_device_file_descriptor.c_str(), O_RDWR);
        bool success = (spi_file_handle >= 0);
    
        const std::uint8_t mode = 3;
        const std::uint8_t bits = 8;
        int ioctl_return = 0;
    
        if (success)
        {
            ioctl_return = ioctl(spi_file_handle, SPI_IOC_WR_MODE, &mode);
            if (ioctl_return != 0)
                success = false;
    
            ioctl_return = ioctl(spi_file_handle, SPI_IOC_RD_MODE, &mode);
            if (ioctl_return != 0)
                success = false;
    
            ioctl_return = ioctl(spi_file_handle, SPI_IOC_WR_BITS_PER_WORD, &bits);
            if (ioctl_return != 0)
                success = false;
    
            ioctl_return = ioctl(spi_file_handle, SPI_IOC_RD_BITS_PER_WORD, &bits);
            if (ioctl_return != 0)
                success = false;
    
            ioctl_return = ioctl(spi_file_handle, SPI_IOC_WR_MAX_SPEED_HZ, &m_speed_bps);
            if (ioctl_return != 0)
                success = false;
    
            ioctl_return = ioctl(spi_file_handle, SPI_IOC_RD_MAX_SPEED_HZ, &m_speed_bps);
            if (ioctl_return != 0)
                success = false;
        }
    
        if (success)
        {
            struct spi_ioc_transfer tr;
            tr.tx_buf = reinterpret_cast<unsigned long>(out_data);
            tr.rx_buf = reinterpret_cast<unsigned long>(in_data);
            tr.len = size_in_bytes;
            tr.delay_usecs = 0;
            tr.speed_hz = 0;
            tr.bits_per_word = 0;
    
            ioctl_return = ioctl(spi_file_handle, SPI_IOC_MESSAGE(1), &tr);
            success = (ioctl_return != 1);
        }
    
        close(spi_file_handle);
    
        return success;
    }
    }
    }
    }
    }
    

    【讨论】:

    • 我的 FPGA 不喜欢它的原因是 FPGA 期待的是模式 0,而不是模式 3
    猜你喜欢
    • 1970-01-01
    • 2017-05-04
    • 1970-01-01
    • 2015-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-18
    相关资源
    最近更新 更多