英特尔凌动 E3900 系列上的 SPIDEV Linux 驱动程序

SPIDEV Linux Driver on Intel Atom E3900 Series

我正在尝试将 Intel E3900 系列(特别是 E3940)的 SPI #2 接口公开为 CentOS 8(内核版本 4.18)的 spidev 接口。作为回退,任何通过 C/C++ API 访问 SPI 控制器的方法都是可以接受的。

我正在尝试确定问题是必须由 BIOS 供应商解决还是我可以使用 ACPI 补丁修复。供应商 (Congatec) 声称 SPI 接口目前不支持作为用户空间实体,但我仍在等待我的问题升级到他们的工程组以确认这一点。供应商还声明 SPI 的 BIOS 设置应保留为“禁用”,但我也尝试了“PCI”和“ACPI”选项,但没有任何变化。

我试图将来自多个参考的片段合并在一起以创建 ACPI 补丁,包括:

https://www.kernel.org/doc/Documentation/acpi/initrd_table_override.txt

https://www.kernel.org/doc/html/latest/firmware-guide/acpi/enumeration.html

我已经重新编译了 CentOS 8 内核以包含所有必要的选项,并且我能够成功重建 Linux initrd(CentOS 8 中的 initramfs)。我通过 dmesg 日志确认我的修改正在启动时加载;我在日志中没有看到任何错误消息,所以我假设它已成功应用。

作为参考,这里是我确保在 (=y) 中编译的内核选项。我计划最终将内核模块与库存内核一起使用,但现在我认为这是更简单的方法。

CONFIG_MFD_INTEL_LPSS
CONFIG_MFD_INTEL_LPSS_ACPI
CONFIG_MFD_INTEL_LPSS_PCI
CONFIG_X86_INTEL_LPSS
CONFIG_SERIAL_8250_LPSS
CONFIG_PWM_LPSS
CONFIG_PWM_LPSS_PCI
CONFIG_PWM_LPSS_PLATFORM
CONFIG_SPI_PXA2XX
CONFIG_SPI_SPIDEV
CONFIG_SPI_BITBANG

当我使用以下命令转储未修改的 ACPI 设备树时,我能够看到对三种不同 SPI 总线的引用,这与它们的 BIOS 设置相关。据我所知,Intel 芯片只有两条 SPI 总线,这让我觉得这确实是需要在他们的 BIOS 中修复的问题。

acpidump >acpidump
acpixtract -a acpidump
iasl -sa *.dat
grep -i spi *.dsl

我已经尝试了几个选项来修补设备树,其中包括我找到的引用和重用 SPI#1 中的设备树配置(我假设它有效),但 none 似乎有效.由于我找到的示例来自 E3800 系列,我希望我只是有一些寄存器设置或引脚标识符错误,需要针对 E3900 系列进行更新。

作为参考,SPI#1 总线用于控制 SOM 上的其他组件,因此我也想避免将其用于一般用途。

提前感谢任何ideas/support。

DefinitionBlock ("spidev.aml", "SSDT", 2, "INTEL ", "SpiDev", 1)
{
    External (_SB_.PCI0.SPI2, DeviceObj)
    
    Scope (\_SB.PCI0.SPI2)
    {
        Device (FPNT)
        {
            Method (_HID, 0, NotSerialized)  // _HID: Hardware ID
            {                
                Return ("FPNT_DIS")
            }
            
            Method (_STA, 0, NotSerialized)  // _STA: Status
            {
                Return (0x0F)
            }
            
            Method (_CRS, 0, Serialized)  // _CRS: Current Resource Settings
            {
                Name (BBUF, ResourceTemplate ()
                {
                    SpiSerialBusV2 (0x0000, PolarityLow, FourWireMode, 0x08,
                        ControllerInitiated, 0x002DC6C0, ClockPolarityLow,
                        ClockPhaseFirst, "\_SB.PCI0.SPI2",
                        0x00, ResourceConsumer, , Exclusive,
                        )
                    GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
                        "\_SB.GPO1", 0x00, ResourceConsumer, ,
                        )
                        {   // Pin list
                            0x0043
                        }
                    GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullDefault, 0x0000,
                        "\_SB.GPO0", 0x00, ResourceConsumer, ,
                        )
                        {   // Pin list
                            0x000E
                        }
                })
                Return (BBUF) /* \_SB_.PCI0.SPI2.FPNT._CRS.BBUF */
            }
        }
    }
}
DefinitionBlock ("spidev.aml", "SSDT", 5, "INTEL", "SPIDEV", 1)
{
  External (_SB_.PCI0.SPI2, DeviceObj)

  Scope (\_SB.PCI0.SPI2)
    {
        Device (TP0) {
            Name (_HID, "SPT0001")
            Name (_DDN, "SPI test device connected to CS2")
            Name (_DSD, Package() {
                    ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                    Package () {
                            Package (2) { "compatible", "spidev" },
                    }
            })
            Name (_CRS, ResourceTemplate () {
                SpiSerialBus (
                    2,                      // Chip select
                    PolarityLow,            // Chip select is active low
                    FourWireMode,           // Full duplex
                    8,                      // Bits per word is 8 (byte)
                    ControllerInitiated,    // Don't care
                    1000000,                // 1 MHz
                    ClockPolarityLow,       // SPI mode 0
                    ClockPhaseFirst,        // SPI mode 0
                    "\_SB.PCI0.SPI2",      // SPI host controller
                    0                       // Must be 0
                )
            })
        }
    }
}
DefinitionBlock ("spidev.aml", "SSDT", 5, "INTEL", "SPIDEV", 1)
{
  External (_SB_.PCI0.SPI2, DeviceObj)

  Scope (\_SB.PCI0.SPI2)
    {
        Device (TP0) {
            Name (_HID, "SPT0001")
            Name (_DDN, "SPI test device connected to CS2")
            Name (_CRS, ResourceTemplate () {
                SpiSerialBus (
                    2,                      // Chip select
                    PolarityLow,            // Chip select is active low
                    FourWireMode,           // Full duplex
                    8,                      // Bits per word is 8 (byte)
                    ControllerInitiated,    // Don't care
                    1000000,                // 1 MHz
                    ClockPolarityLow,       // SPI mode 0
                    ClockPhaseFirst,        // SPI mode 0
                    "\_SB.PCI0.SPI2",      // SPI host controller
                    0                       // Must be 0
                )
            })
        }
    }
}

编辑:在通过 initrd

应用上述内容后添加了 dmesg 输出
[    0.000000] BRK [0x63ef9000, 0x63ef9fff] PGTABLE
[    0.000000] BRK [0x63efa000, 0x63efafff] PGTABLE
[    0.000000] RAMDISK: [mem 0x3b4f6000-0x3ce5cfff]
[    0.000000] ACPI: SSDT ACPI table found in initrd [kernel/firmware/acpi/spidev.aml][0xb7]
[    0.000000] modified physical RAM map:
[    0.000000] modified: [mem 0x0000000000000000-0x0000000000000fff] reserved
[    0.000000] modified: [mem 0x0000000000001000-0x000000000003efff] usable
--
[    0.000000] ACPI: UEFI 0x00000000798C8400 000042 (v01 ALASKA A M I    00000000      00000000)
[    0.000000] ACPI: TPM2 0x00000000798C8450 000034 (v04 ALASKA A M I    00000001 AMI  00000000)
[    0.000000] ACPI: WDAT 0x00000000798C8490 000104 (v01                 00000000      00000000)
[    0.000000] ACPI: Table Upgrade: install [SSDT- INTEL-  SPIDEV]
[    0.000000] ACPI: SSDT 0x00000000774A2000 0000B7 (v05 INTEL  SPIDEV   00000001 INTL 20180629)
[    0.000000] ACPI: Local APIC address 0xfee00000
[    0.000000] No NUMA configuration found
[    0.000000] Faking a node at [mem 0x0000000000000000-0x000000017fffffff]
--
[    1.141553] dw-apb-uart.0: ttyS4 at MMIO 0x91326000 (irq = 4, base_baud = 115200) is a 16550A
[    1.144263] dw-apb-uart.1: ttyS5 at MMIO 0x91324000 (irq = 5, base_baud = 115200) is a 16550A
[    1.146886] dw-apb-uart.2: ttyS6 at MMIO 0x91322000 (irq = 6, base_baud = 115200) is a 16550A
[    1.149799] pxa2xx-spi pxa2xx-spi.4: cs2 >= max 2
[    1.151063] spi_master spi2: failed to add SPI device SPT0001:00 from ACPI
[    1.153366] rdac: device handler registered
[    1.154791] hp_sw: device handler registered
[    1.156043] emc: device handler registered

[root@localhost ~]# ls /dev/
autofs           fuse       log                 nvram     tty    tty25  tty42  tty6    ttyS5    vcsa1
block            gpiochip0  loop-control        port      tty0   tty26  tty43  tty60   ttyS6    vcsa2
bus              gpiochip1  mapper              ppp       tty1   tty27  tty44  tty61   ttyS7    vcsa3
char             gpiochip2  mcelog              pps0      tty10  tty28  tty45  tty62   ttyS8    vcsa4
console          gpiochip3  mei0                pps1      tty11  tty29  tty46  tty63   ttyS9    vcsa5
core             hidraw0    mem                 ptmx      tty12  tty3   tty47  tty7    uhid     vcsa6
cpu              hpet       memory_bandwidth    ptp0      tty13  tty30  tty48  tty8    uinput   vfio
cpu_dma_latency  hugepages  mmcblk1             ptp1      tty14  tty31  tty49  tty9    urandom  vga_arbiter
cs               hwrng      mmcblk1boot0        pts       tty15  tty32  tty5   ttyS0   usbmon0  vhci
cuse             i2c-0      mmcblk1boot1        random    tty16  tty33  tty50  ttyS1   usbmon1  vhost-net
disk             i2c-1      mmcblk1p1           raw       tty17  tty34  tty51  ttyS10  usbmon2  vhost-vsock
dm-0             i2c-2      mmcblk1p2           rtc       tty18  tty35  tty52  ttyS11  vcs      zero
dm-1             i2c-3      mmcblk1p3           rtc0      tty19  tty36  tty53  ttyS12  vcs1
dri              i2c-4      mmcblk1rpmb         shm       tty2   tty37  tty54  ttyS13  vcs2
drm_dp_aux0      i2c-5      mqueue              snapshot  tty20  tty38  tty55  ttyS14  vcs3
drm_dp_aux1      initctl    net                 snd       tty21  tty39  tty56  ttyS15  vcs4
fb0              input      network_latency     stderr    tty22  tty4   tty57  ttyS2   vcs5
fd               kmsg       network_throughput  stdin     tty23  tty40  tty58  ttyS3   vcs6
full             kvm        null                stdout    tty24  tty41  tty59  ttyS4   vcsa

编辑:添加请求的tables.dat输出 https://pastebin.com/TBj8LRVc

编辑:添加了请求的状态输出

[root@localhost ~]# grep -H 15 /sys/bus/acpi/devices/*/status
/sys/bus/acpi/devices/device:19/status:15
/sys/bus/acpi/devices/device:1a/status:15
/sys/bus/acpi/devices/device:1d/status:15
/sys/bus/acpi/devices/device:3e/status:15
/sys/bus/acpi/devices/device:44/status:15
/sys/bus/acpi/devices/device:45/status:15
/sys/bus/acpi/devices/INT33A1:00/status:15
/sys/bus/acpi/devices/INT3452:00/status:15
/sys/bus/acpi/devices/INT3452:01/status:15
/sys/bus/acpi/devices/INT3452:02/status:15
/sys/bus/acpi/devices/INT3452:03/status:15
/sys/bus/acpi/devices/INT3511:00/status:15
/sys/bus/acpi/devices/INT3512:00/status:15
/sys/bus/acpi/devices/LNXPOWER:00/status:15
/sys/bus/acpi/devices/MSFT0101:00/status:15
/sys/bus/acpi/devices/PNP0103:00/status:15
/sys/bus/acpi/devices/PNP0C0D:00/status:15
/sys/bus/acpi/devices/PNP0C0E:00/status:15

编辑:添加请求的 lspci 输出

[root@localhost ~]# lspci -nk -s 19
00:19.0 1180: 8086:5ac2 (rev 0b)
        Subsystem: 8086:7270
        Kernel driver in use: intel-lpss
00:19.1 1180: 8086:5ac4 (rev 0b)
        Subsystem: 8086:7270
        Kernel driver in use: intel-lpss
00:19.2 1180: 8086:5ac6 (rev 0b)
        Subsystem: 8086:7270
        Kernel driver in use: intel-lpss

感谢0andriy!他让我越过了障碍,一路上教了我一些新命令。事实证明,我的问题的根本原因有两方面:

  1. 电路板供应商警告我不要在 BIOS 中启用 SPI#1,因为该总线用于控制 SoM 本身的项目(假设通过他们的 Linux BSP/driver ?)。我必须在 ACPI 模式下启用所有三个 SPI 接口才能加载它们并显示在 lspci -nk -s 19 输出中。

  2. 设备树更新文件有一个错误,我之前错过了这个错误,因为接口本身没有被加载。 AML 文件需要指定 Chip Select 1,而不是 2。

下面的脚本将进行所有 initrd 更改并使用 SPIDEV 公开所有三个 SPI 总线。在我正在测试的板上,SPI 总线作为 spidev1 通过。

我还需要确认E3900可以处理的最大速度,但我认为其他参数设置正确。

#!/bin/bash
#
# SCRIPT NAME: ENABLE SPIDEV ON INTEL ATOM E3900 SERIES SOC
# TARGET PLATFORM: CENTOS8_x86-64
# AUTHOR: ADAM ACKERMAN
# LICENSE: MIT
#
# REFERENCES:
# https://www.kernel.org/doc/Documentation/acpi/initrd_table_override.txt
# 
# https://www.kernel.org/doc/html/latest/firmware-guide/acpi/enumeration.html
#

# Pull current kernel version
KERNEL_VER=$(cat /proc/version | cut -d " " -f 3)
# Verify current kernel includes spidev support
# NOTE: If configured as module, must be actively loaded
if [[ ! -d /sys/class/spidev ]]; then
  modprobe spidev
  if [[ ! -d /sys/class/spidev ]]; then
    echo "Kernel does not support SPIDEV. Please enable first."
    exit 1
  fi
fi
# Move the backup file back to active, if exists
if [[ -f /boot/initramfs-$KERNEL_VER.img.bak ]]; then
  rm -f /boot/initramfs-$KERNEL_VER.img
  mv /boot/initramfs-$KERNEL_VER.img.bak /boot/initramfs-$KERNEL_VER.img
fi
# Create new temp directory and change to it
ACPI_TMP=$(mktemp -d)
cd $ACPI_TMP
# Reference commands to pull current ACPI tree
#acpidump >acpidump
#acpixtract -a acpidump
#iasl -sa *.dat
#grep -i spi *.dsl
# Paste in ASL file to enable the SPIDEV interface
cat > spidev.asl <<'_EOF'
DefinitionBlock ("spidev.aml", "SSDT", 5, "INTEL", "SPIDEV", 1)
{
    External (_SB_.PCI0.SPI1, DeviceObj)

    Scope (\_SB.PCI0.SPI1)
    {
        Device (TP10) {
            Name (_HID, "SPT0001")
            Name (_DDN, "SPI1-CS0")
            Name (_CRS, ResourceTemplate () {
                SpiSerialBus (
                    0,                      // Chip select
                    PolarityLow,            // Chip select is active low
                    FourWireMode,           // Full duplex
                    8,                      // Bits per word is 8 (byte)
                    ControllerInitiated,    // Don't care
                    1000000,                // 1 MHz
                    ClockPolarityLow,       // SPI mode 0
                    ClockPhaseFirst,        // SPI mode 0
                    "\_SB.PCI0.SPI1",      // SPI host controller
                    0                       // Must be 0
                )
            })
        }

        Device (TP11) {
            Name (_HID, "SPT0001")
            Name (_DDN, "SPI1-CS1")
            Name (_CRS, ResourceTemplate () {
                SpiSerialBus (
                    1,                      // Chip select
                    PolarityLow,            // Chip select is active low
                    FourWireMode,           // Full duplex
                    8,                      // Bits per word is 8 (byte)
                    ControllerInitiated,    // Don't care
                    1000000,                // 1 MHz
                    ClockPolarityLow,       // SPI mode 0
                    ClockPhaseFirst,        // SPI mode 0
                    "\_SB.PCI0.SPI1",      // SPI host controller
                    0                       // Must be 0
                )
            })
        }
    }

    External (_SB_.PCI0.SPI2, DeviceObj)

    Scope (\_SB.PCI0.SPI2)
    {
        Device (TP20) {
            Name (_HID, "SPT0001")
            Name (_DDN, "SPI2-CS0")
            Name (_CRS, ResourceTemplate () {
                SpiSerialBus (
                    0,                      // Chip select
                    PolarityLow,            // Chip select is active low
                    FourWireMode,           // Full duplex
                    8,                      // Bits per word is 8 (byte)
                    ControllerInitiated,    // Don't care
                    1000000,                // 1 MHz
                    ClockPolarityLow,       // SPI mode 0
                    ClockPhaseFirst,        // SPI mode 0
                    "\_SB.PCI0.SPI2",      // SPI host controller
                    0                       // Must be 0
                )
            })
        }

        Device (TP21) {
            Name (_HID, "SPT0001")
            Name (_DDN, "SPI2-CS1")
            Name (_CRS, ResourceTemplate () {
                SpiSerialBus (
                    1,                      // Chip select
                    PolarityLow,            // Chip select is active low
                    FourWireMode,           // Full duplex
                    8,                      // Bits per word is 8 (byte)
                    ControllerInitiated,    // Don't care
                    1000000,                // 1 MHz
                    ClockPolarityLow,       // SPI mode 0
                    ClockPhaseFirst,        // SPI mode 0
                    "\_SB.PCI0.SPI2",      // SPI host controller
                    0                       // Must be 0
                )
            })
        }
    }

    External (_SB_.PCI0.SPI3, DeviceObj)

    Scope (\_SB.PCI0.SPI3)
    {
        Device (TP30) {
            Name (_HID, "SPT0001")
            Name (_DDN, "SPI3-CS0")
            Name (_CRS, ResourceTemplate () {
                SpiSerialBus (
                    0,                      // Chip select
                    PolarityLow,            // Chip select is active low
                    FourWireMode,           // Full duplex
                    8,                      // Bits per word is 8 (byte)
                    ControllerInitiated,    // Don't care
                    1000000,                // 1 MHz
                    ClockPolarityLow,       // SPI mode 0
                    ClockPhaseFirst,        // SPI mode 0
                    "\_SB.PCI0.SPI3",      // SPI host controller
                    0                       // Must be 0
                )
            })
        }

        Device (TP31) {
            Name (_HID, "SPT0001")
            Name (_DDN, "SPI3-CS1")
            Name (_CRS, ResourceTemplate () {
                SpiSerialBus (
                    1,                      // Chip select
                    PolarityLow,            // Chip select is active low
                    FourWireMode,           // Full duplex
                    8,                      // Bits per word is 8 (byte)
                    ControllerInitiated,    // Don't care
                    1000000,                // 1 MHz
                    ClockPolarityLow,       // SPI mode 0
                    ClockPhaseFirst,        // SPI mode 0
                    "\_SB.PCI0.SPI3",      // SPI host controller
                    0                       // Must be 0
                )
            })
        }
    }
}
_EOF
# Convert the ASL file to AML
iasl spidev.asl
# Create new directory structure to match initrd format
mkdir -p kernel/firmware/acpi
# Copy in the AML file
cp spidev.aml kernel/firmware/acpi
# Load all files into a new initrd in /boot
find kernel | cpio -H newc --create > /boot/instrumented_initrd
# Move out of the temporary directory and remove
cd ~
rm -rf $ACPI_TMP
# Merge the current initrd to the end of the one just created
cat /boot/initramfs-$KERNEL_VER.img >>/boot/instrumented_initrd
# Move the working one to a backup location
mv /boot/initramfs-$KERNEL_VER.img /boot/initramfs-$KERNEL_VER.img.bak
# Move the new one into place
mv /boot/instrumented_initrd /boot/initramfs-$KERNEL_VER.img
# Script Finished
echo "Process Complete - reboot the system for the changes to take effect."
echo "After reboot, verify success with command 'dmesg | grep -i spi'"

生成的设备列表是:

[root@localhost ~]# ls /dev/spi*
/dev/spidev1.0  /dev/spidev1.1  /dev/spidev2.0  /dev/spidev2.1  /dev/spidev3.0  /dev/spidev3.1