1

I am attempting to expose the SPI #2 interface from the Intel E3900 series (specifically the E3940) as a spidev interface to CentOS 8 (kernel version 4.18). As a fallback, any method to access the SPI controller through a C/C++ API would be acceptable.

I am trying to determine if the issue is something that must be corrected by the BIOS vendor or something I can fix with an ACPI patch. The vendor (Congatec) has claimed that the SPI interface is currently not supported as a userspace entity, but I am still waiting for my issue to be escalated to their engineering group to confirm that. The vendor also stated that the BIOS setting for the SPIs should be left at "Disabled", but I have also tried the "PCI" and "ACPI" options with no change.

I have attempted to merge together snippets from several references to create an ACPI patch, including:

spidev Linux driver on Intel Atom board

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

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

I have recompiled the CentOS 8 kernel to include all the necessary options, and I am able to successfully rebuild the Linux initrd (initramfs in CentOS 8). I confirmed through dmesg logs that my modification is being loaded at boot; I don't see any error messages in the logs, so I am assuming that it is being applied successfully.

For reference, here are the kernel options I have ensured are compiled in (=y). I plan to eventually use kernel modules in concert with the stock kernel, but for now I thought this was the easier path.

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

When I dump the unmodified ACPI device tree with the following commands, I am able to see references to three different SPI buses, which correlate with their BIOS settings. As far as I know, the Intel chip only includes two SPI buses, which makes me think this really is something that will need to be fixed in their BIOS.

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

I have tried several options to patch the device tree that include both references I found and reusing the device-tree configuration from SPI#1 (which I am assuming works), but none have seemed to work. Since the examples I have found are from the E3800 series, I'm hoping that I just have some register setting or pin identifier wrong and it needs to be updated for the E3900 series.

For reference, the SPI#1 bus is used to control other components on the SOM, so I want to avoid using that for general purpose as well.

Thanks in advance for any 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
                )
            })
        }
    }
}

EDIT: Added dmesg output after applying above via initrd

[    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

EDIT: Added requested tables.dat output https://pastebin.com/TBj8LRVc

EDIT: Added requested status output

[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

EDIT: Added requested lspci output

[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
  • 4,183
  • 1
  • 24
  • 37
drkwood
  • 73
  • 1
  • 7
  • To get your examples working you need to check the correctness of the path to SPI controllers. Btw, your assumption that SPI controllers can be only 0,1 or 2 is incorrect. As many as SoC has them on silicon, Not all of them, though, enabled in the BIOS (it depends on OEM). Also it would be nice to see the ACPI *tables.dat* file from `acpidump -o tables.dat`. On top of this the kernel version makes difference. For 3.x series probably nothing will work except the patching the code with some ugly hacks. – 0andriy Nov 15 '19 at 20:56
  • Added requested tables.dat output (https://pastebin.com/TBj8LRVc). I neglected to add the kernel version for CentOS8 the first time, which is 4.18 – drkwood Nov 15 '19 at 21:43
  • 1
    Forget to mention that the actual exposed devices can be listed by running `grep -H 15 /sys/bus/acpi/devices/*/status`. Would be nice to see if the SPI controllers are there. – 0andriy Nov 15 '19 at 22:05
  • Okay, according to the dump you have three controllers, that are enumerated by PCI (`lspci -nk -s 19` should show them). It might be the driver (*spi-pxa2xx-pci.c*) doesn't support those ID's. Please add the `lspci -nk` output. – 0andriy Nov 15 '19 at 22:19
  • Added requested output, and confirmed that the output was not affected by the "Disabled | PCI | ACPI" BIOS settings for SPI#1 through SPI#3. – drkwood Nov 15 '19 at 22:19
  • Added requested output, and it looks like they are loaded. Those device IDs match up with the output from https://cateee.net/lkddb/web-lkddb/SPI.html – drkwood Nov 15 '19 at 22:24
  • Yes, It's Intel Broxton SoC and SPI driver for PCI is actually *intel-lpss-pci.c*. You have driver very well loaded. Now, you simple need to add the excerpt I posted in mentioned SO question (SPIDEV aml code) either to initramfs or load thru configfs (latter requires the ACPI configfs support to be enabled in the kernel, first one though requires another option to be set, it can be both, they are not conflicting). – 0andriy Nov 15 '19 at 22:25
  • That's what I run on real Broxton machine `acpi-table-load /acpi-tables/spidev0.aml [ 49.755626] ACPI: Host-directed Dynamic ACPI Table Load: [ 49.761656] ACPI: SSDT 0xFFFF9B6A3961E6C0 0000B7 (v05 SPIDEV0 00000001 INTL 20190703) [ 49.789415] spidev spi-SPT0001:00: do not use this driver in production systems!` And I have got the device: `ls -l /dev/spidev2.0 crw-rw---- 1 root root 153, 0 Nov 15 22:35 /dev/spidev2.0`. Modules I have got: `lsmod ... spidev 24576 0 acpi_configfs 16384 2 ...` – 0andriy Nov 15 '19 at 22:39
  • Unfortunately the device with CS2 can't be added: `[ 199.648543] ACPI BIOS Error (bug): Failure creating named object [\_SB.PCI0.SPI2.TP0], AE_ALREADY_EXISTS (20191018/dswload2-323)`. It is conflicting with existing device on the board I have (TP0). – 0andriy Nov 15 '19 at 22:41

1 Answers1

3

Thanks to 0andriy! He got me past the roadblock and taught me a few new commands along the way. The root cause of my issue was two-fold as it turned out:

  1. The board vendor had cautioned me against enabling SPI#1 in BIOS, as that bus is used to control items on the SoM itself (assuming via their Linux BSP/driver?). I had to enable all three SPI interfaces in ACPI mode to have them be loaded and show up in the lspci -nk -s 19 output.

  2. The device-tree update file had an error, which I missed previously because the interface itself was not being loaded. The AML file needed to specify Chip Select 1, not 2.

The script below will make all of the initrd changes and expose all three SPI buses using SPIDEV. On the board I am testing with, the SPI bus is coming through as spidev1.

I still need to confirm the Maximum speed the E3900 can handle, but I think the other parameters are set correctly.

#!/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://stackoverflow.com/questions/39118721/spidev-linux-driver-on-intel-atom-board
# 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'"

The resulting device list is:

[root@localhost ~]# ls /dev/spi*
/dev/spidev1.0  /dev/spidev1.1  /dev/spidev2.0  /dev/spidev2.1  /dev/spidev3.0  /dev/spidev3.1
0andriy
  • 4,183
  • 1
  • 24
  • 37
drkwood
  • 73
  • 1
  • 7